I need to make a template specialization which distinguish between a template class with (only) value parameters like the following:
template<auto ... __vz>
struct values{};
and a template class with (only) reference parameters like the following:
template<auto& ... __lvz>
struct lvalues{};
But with following specialization I was not successful:
template<typename>
struct is_values{
static constexpr bool value = false;
};
template<template<auto ...>class __m, auto ... __vz>
struct is_values<__m<__vz ...> >{
static constexpr bool value = true;
};
template<typename>
struct is_lvalues{
static constexpr bool value = false;
};
template<template<auto& ...>class __m, auto& ... __vz>
struct is_lvalues<__m<__vz ...> >{
static constexpr bool value = true;
};
When they are tested like the following:
const int a = 1;
const int b = 2;
const int c = 3;
int main()
{
std::cout << is_values<lvalues<a,b,c> >::value <<std::endl; // wrong result
std::cout << is_lvalues<values<1,2,3> >::value <<std::endl; // causes error
}
One of them gives a wrong result and one gives the following error:
test.cpp: In instantiation of ‘struct is_lvalues<values<1, 2, 3> >’:
test.cpp:51:41: required from here
test.cpp:32:34: error: initializing ‘int&’ with ‘int’ in converted constant expression does not bind directly
32 | struct is_lvalues<__m<__vz ...> >{
| ^
test.cpp:32:34: error: could not convert ‘1’ from ‘int’ to ‘int&’
test.cpp:32:34: error: initializing ‘int&’ with ‘int’ in converted constant expression does not bind directly
test.cpp:32:34: error: could not convert ‘2’ from ‘int’ to ‘int&’
test.cpp:32:34: error: initializing ‘int&’ with ‘int’ in converted constant expression does not bind directly
test.cpp:32:34: error: could not convert ‘3’ from ‘int’ to ‘int&’
test.cpp:33:24: error: initializing ‘int&’ with ‘int’ in converted constant expression does not bind directly
33 | static constexpr bool value = true;
| ^~~~~
test.cpp:33:24: error: could not convert ‘1’ from ‘int’ to ‘int&’
test.cpp:33:24: error: initializing ‘int&’ with ‘int’ in converted constant expression does not bind directly
test.cpp:33:24: error: could not convert ‘2’ from ‘int’ to ‘int&’
test.cpp:33:24: error: initializing ‘int&’ with ‘int’ in converted constant expression does not bind directly
test.cpp:33:24: error: could not convert ‘3’ from ‘int’ to ‘int&’
test.cpp: In function ‘int main()’:
test.cpp:51:43: error: ‘value’ is not a member of ‘is_lvalues<values<1, 2, 3> >’
51 | std::cout << is_lvalues<values<1,2,3> >::value <<std::endl; // causes error
| ^~~~~
make: *** [src/subdir.mk:20: src/test.o] Error 1
"make all" terminated with exit code 2. Build might be incomplete.
12:14:37 Build Failed. 14 errors, 0 warnings. (took 677ms)
So far I was only successful with the following specializations:
template<auto ... __vz>
struct is_values<values<__vz ...> >{
static constexpr bool value = true;
};
template<auto& ... __vz>
struct is_lvalues<lvalues<__vz ...> >{
static constexpr bool value = true;
};
But these are not desired because they only detect the types resulted from values
and lvalues
not any other similar template with different name.
So what is the right specialization?
If you're solving this for types, you can use
std::is_lvalue_reference
to do this for a single type. Since you seem to be using C++17, you can use fold expressions to apply this to a parameter pack:
template <typename... Ts>
using are_types_lvalues = std::integral_constant<bool, (std::is_lvalue_reference_v<Ts> && ...)>;
template <typename... Ts>
using are_types_values = std::integral_constant<bool, !(std::is_reference_v<Ts> || ...)>;
If you can test the types, you can just use decltype
to get a non-type parameter's type, and not worry about forcing an argument to bind to auto
or auto&
...
template<typename>
struct are_values : std::false_type {};
template<template<auto ...>class __m, auto ... __vz>
struct are_values<__m<__vz ...>> : std::integral_constant<bool, !(std::is_reference_v<decltype(__vz)> || ...)> {};
template<typename>
struct are_lvalues : std::false_type {};
template<template<auto ...>class __m, auto ... __vz>
struct are_lvalues<__m<__vz ...>> : std::integral_constant<bool, (std::is_lvalue_reference_v<decltype(__vz)> && ...)> {};
usage:
template<auto... __vz>
struct values{};
template<auto& ... __lvz>
struct lvalues{};
const int a = 1;
const int b = 2;
const int c = 3;
static_assert(are_values<values<1,2,3>>::value, "");
static_assert(!are_values<lvalues<a,b,c>>::value, "");
static_assert(!are_lvalues<values<1,2,3>>::value, "");
static_assert(are_lvalues<lvalues<a,b,c>>::value, "");