Can I use a variable template to declare another variable template? - c++

With variable templates coming in C++14 (and Clang already supporting them) and a proposal for standard is_same_v and likewise type traits, I figured being able to make new type traits as follows would be neat:
template<typename T>
constexpr bool is_const_and_volatile{std::is_const_v<T> && std::is_volatile_v<T>};
Alas, this results in errors equivalent to the following SSCCE (this one contains everything mentioned below):
#include <type_traits>
template<typename T>
constexpr bool is_pointer{std::is_pointer<T>::value};
template<typename T>
constexpr bool foo{is_pointer<T>};
int main() {
//foo<int *>;
}
With the line in main commented, Clang spits out the following:
warning: variable is_pointer<type-parameter-0-0> has internal linkage but is not defined
It looks defined to me (note that changing T to int * in foo works fine). Uncommenting the line in main to instantiate foo gives this (again, T to int * works fine):
error: constexpr variable foo<int *> must be initialized by a constant expression
However, replacing foo with the following old syntax causes both instances to work fine:
constexpr bool foo{std::is_pointer<T>::value};
Is there something I'm missing about variable templates? Is there a way to build new variable templates with them, or am I forced to use the older syntax to build new ones and only enjoy the syntactic sugar when using them for other code?

Your code is valid, and is accepted by clang SVN. The link error was caused by clang bug 17846, which I fixed a couple of days ago.

The following seems to work:
#include <type_traits>
#include <iostream>
template<typename T>
struct test {
static constexpr bool is_pointer{std::is_pointer<T>::value};
};
template<typename T>
constexpr bool test<T>::is_pointer;
template<typename T>
constexpr bool foo{test<T>::is_pointer};
int main() {
std::cout << foo<bool>;
std::cout << foo<bool*>;
}
Live Example
Although it procs the same warning if used in a constexpr context so I suppose it doesn't really work after all.
// Fail
template<typename T>
typename std::enable_if<foo<T>, void>::type bar()
{
}
int main() {
bar<bool*>();
}
main.cpp:21:5: error: no matching function for call to 'bar'
bar<bool*>();
^~~~~~~~~~
main.cpp:16:45: note: candidate template ignored: substitution failure [with T = bool *]: non-type template argument is not a constant expression
typename std::enable_if<foo<T>, void>::type bar()
It does stop complaining if you give foo an explicit type:
template<typename T>
typename std::enable_if<foo<bool*>, void>::type bar()
{
}
Or just use test<T>::is_pointer directly:
template<typename T>
typename std::enable_if<test<T>::is_pointer, void>::type bar()
{
}

Related

Why can't a member function require a static constexpr member of the same class to be true?

I try to have a member function require a static constexpr boolean member to be true. This would be very helpful to DRY a quite complex requirement. And I do not quire get the reason why the compiler won't let me.
Minimal example with slightly less complex requirement:
template <typename T>
struct Foo
{
static constexpr bool isInt = std::integral<T>;
void bar() requires (isInt);
void goo() requires std::integral<T>;
};
template <typename T>
void Foo<T>::bar() requires (Foo<T>::isInt) // error: out-of-line definition of 'bar' does not match any declaration in 'Foo<T>' x86-64 clang 14.0.0 #1
{
// ...
}
template <typename T>
void Foo<T>::goo() requires std::integral<T> // ok
{
// ...
}
Is this because isInt is declared inside the same class? Or do I have some kind of syntax error?
There is a question of whether or not your requires clauses in the in-class declaration and out-of-class definition are equivalent (or functionally equivalent).
But regardless of the answer to that question, the easiest solution is to simply use the exact same token sequence, which will be certain to be equivalent:
template <typename T>
void Foo<T>::bar() requires (isInt)
{
// ...
}
However, current Clang releases don't accept any variations I try to name the static member, especially not those using the same token sequence in both declaration and definition. At least that last part is definitively a bug.
But on current Clang trunk all variations are accepted, indicating that this is a recently fixed bug.
Seems like an MSVC bug. Here's a workaround:
#include <concepts>
template <class T>
concept isInt = std::integral<T>;
template <typename T>
struct Foo
{
void bar() requires (isInt<T>);
};
template <typename T>
void Foo<T>::bar() requires (isInt<T>)
{
}
https://godbolt.org/z/h6enoGqG6

C++14: Calling a static constexpr template method of template class in `std::enable_if [duplicate]

The following doesn't compile under g++ 8.1.0 on CentOS 7:
hey.h
#pragma once
#include <iostream>
#include <type_traits>
class Valid {};
class Invalid {};
struct Hey
{
template<typename T>
static constexpr bool is_valid() { return std::is_same_v<T, Valid>; }
template<typename T, std::enable_if_t<is_valid<T>()>* = nullptr>
void howdy() const;
};
template<typename T, std::enable_if_t<Hey::is_valid<T>()>*>
void Hey::howdy() const
{
std::cout << "Howdy" << std::endl;
}
Compiler output:
In file included from hey.cpp:1:
hey.h:18:8: error: no declaration matches ‘void Hey::howdy() const’
void Hey::howdy() const
^~~
hey.h:14:10: note: candidate is: ‘template<class T, std::enable_if_t<is_valid<T>()>* <anonymous> > void Hey::howdy() const’
void howdy() const;
^~~~~
hey.h:8:8: note: ‘struct Hey’ defined here
struct Hey
^~~
Amazingly, all I have to do to both compile correctly and get the desired behavior is add a typedef in Hey:
hey.h (fixed, first few boring lines skipped)
struct Hey
{
template<typename T>
static constexpr bool is_valid() { return std::is_same_v<T, Valid>; }
template<typename T>
using EnableType = std::enable_if_t<is_valid<T>()>;
template<typename T, EnableType<T>* = nullptr>
void howdy() const;
};
template<typename T, Hey::EnableType<T>*>
void Hey::howdy() const
{
std::cout << "Howdy" << std::endl;
}
hey.cpp
#include "hey.h"
int main(int, char**)
{
Hey hey;
hey.howdy<Valid>();
// Adding this line breaks the build, as it should:
// hey.howdy<Invalid>();
return 0;
}
After many tweaks, I've narrowed the compiler error situation to the fact that 1) is_valid() is a member of Hey and 2) howdy() is declared inside Hey's body but defined outside. If you delete the using and make is_valid() a standalone function outside of Hey, there are no issues compiling. If you delete the using and define howdy() inside the class definition, there are also no issue compiling. But when howdy() is defined outside of the class definition, is_valid() is declared inside of the class definition, and the using isn't present, the compiler fails. Is this correct behavior? Am I looking at a compiler bug?
The matching of expressions in template declarations is based on equivalence, a concept based on the one-definition rule. For two expressions to be considered equivalent, they must be at least token-by-token identical modulo the renaming of template parameters.
The expressions is_valid<T>() and Hey::is_valid<T>() are not equivalent (the second has two tokens the first doesn't have), and so the compiler is not required to match them.
Hey::EnableType<T> is a type, and is not subject to the strict equivalence rules for expressions.

Forward declared functions and SFINAE

I've encountered an interesting pattern in the unconstexpr library.
According to the readme, the idea is that given a forward declared auto return type function, the return type can't be deduced before the function is actually defined, and this allows some SFINAE magic.
I thought this language feature could be useful for a few other ideas, and did some experiments with it, and it looks like gcc and clang behaves differently when the function isn't a template: gcc works the same, while clang reports an error.
And based on what the error message of clang shows, maybe it shouldn't work even for a template?
So the questions are:
is the template part (allowed by both clang and gcc) allowed by the standard?
which compiler is correct about the non template version?
Simplified code (also on godbolt):
struct S {};
constexpr auto f1();
template <typename T>
constexpr auto f2(T);
// compilation error with clang:
// function with deduced return type cannot be used before it is defined
template <typename T, typename = decltype(f1())>
void test1_1() {}
template <typename T>
void test1_1() {}
// compiles with clang, even if f2 will never be defined
template <typename T, typename = decltype(f2<T>(T{}))>
constexpr int test1_2(int) { return 1; }
template <typename T>
constexpr int test1_2(float) { return 2; }
/////////////////
int g1_2() {
return test1_2<S>(0);
}
constexpr auto f1() { return 1; }
// comment-uncomment this:
// the value returned by g2_# will change
// /*
template <typename T>
constexpr auto f2(T) {
return 2;
}
// */
// can be defined here
template <typename T, typename = decltype(f1())>
void test2_1() {}
int g2_2() {
return test1_2<S>(0);
}
Note: originally I also mentioned that it behaves differently with friends, and that it seems to be working without an auto return type - those were user errors on my part.

Separate definition and declaration of template member function using enable_if whose template parameter also includes a constexpr member function

The following doesn't compile under g++ 8.1.0 on CentOS 7:
hey.h
#pragma once
#include <iostream>
#include <type_traits>
class Valid {};
class Invalid {};
struct Hey
{
template<typename T>
static constexpr bool is_valid() { return std::is_same_v<T, Valid>; }
template<typename T, std::enable_if_t<is_valid<T>()>* = nullptr>
void howdy() const;
};
template<typename T, std::enable_if_t<Hey::is_valid<T>()>*>
void Hey::howdy() const
{
std::cout << "Howdy" << std::endl;
}
Compiler output:
In file included from hey.cpp:1:
hey.h:18:8: error: no declaration matches ‘void Hey::howdy() const’
void Hey::howdy() const
^~~
hey.h:14:10: note: candidate is: ‘template<class T, std::enable_if_t<is_valid<T>()>* <anonymous> > void Hey::howdy() const’
void howdy() const;
^~~~~
hey.h:8:8: note: ‘struct Hey’ defined here
struct Hey
^~~
Amazingly, all I have to do to both compile correctly and get the desired behavior is add a typedef in Hey:
hey.h (fixed, first few boring lines skipped)
struct Hey
{
template<typename T>
static constexpr bool is_valid() { return std::is_same_v<T, Valid>; }
template<typename T>
using EnableType = std::enable_if_t<is_valid<T>()>;
template<typename T, EnableType<T>* = nullptr>
void howdy() const;
};
template<typename T, Hey::EnableType<T>*>
void Hey::howdy() const
{
std::cout << "Howdy" << std::endl;
}
hey.cpp
#include "hey.h"
int main(int, char**)
{
Hey hey;
hey.howdy<Valid>();
// Adding this line breaks the build, as it should:
// hey.howdy<Invalid>();
return 0;
}
After many tweaks, I've narrowed the compiler error situation to the fact that 1) is_valid() is a member of Hey and 2) howdy() is declared inside Hey's body but defined outside. If you delete the using and make is_valid() a standalone function outside of Hey, there are no issues compiling. If you delete the using and define howdy() inside the class definition, there are also no issue compiling. But when howdy() is defined outside of the class definition, is_valid() is declared inside of the class definition, and the using isn't present, the compiler fails. Is this correct behavior? Am I looking at a compiler bug?
The matching of expressions in template declarations is based on equivalence, a concept based on the one-definition rule. For two expressions to be considered equivalent, they must be at least token-by-token identical modulo the renaming of template parameters.
The expressions is_valid<T>() and Hey::is_valid<T>() are not equivalent (the second has two tokens the first doesn't have), and so the compiler is not required to match them.
Hey::EnableType<T> is a type, and is not subject to the strict equivalence rules for expressions.

SFINAE and template function instantiation: Why a template argument cannot be deduced when used in function arguments with a SFINAE-enabled type?

I was experimenting with SFINAE these days, and something puzzles me. Why my_type_a cannot be deduced in my_function's instantiation?
class my_type_a {};
template <typename T>
class my_common_type {
public:
constexpr static const bool valid = false;
};
template <>
class my_common_type<my_type_a> {
public:
constexpr static const bool valid = true;
using type = my_type_a;
};
template <typename T> using my_common_type_t = typename my_common_type<T>::type;
template <typename T, typename V>
void my_function(my_common_type_t<T> my_cvalue, V my_value) {}
int main(void) {
my_function(my_type_a(), 1.0);
}
G++ gives me this:
/home/flisboac/test-template-template-arg-subst.cpp: In function ‘int main()’:
/home/flisboac/test-template-template-arg-subst.cpp:21:30: error: no matching function for call to ‘my_function(my_type_a, double)’
my_function(my_type_a(), 1.0);
^
/home/flisboac/test-template-template-arg-subst.cpp:18:6: note: candidate: template<class T, class V> void my_function(my_common_type_t<T>, V)
void my_function(my_common_type_t<T> my_type, V my_value) {}
^~~~~~~~~~~
/home/flisboac/test-template-template-arg-subst.cpp:18:6: note: template argument deduction/substitution failed:
/home/flisboac/test-template-template-arg-subst.cpp:21:30: note: couldn't deduce template parameter ‘T’
my_function(my_type_a(), 1.0);
^
What I expected was that, when calling my_function as I did in main, T would be deduced to the type of the function's first argument, and that type would be used in the function's instantiation. But it seems that my_common_type_t<T> is instantiated before the function, but even then, the type of my_cvalue would become my_type_a anyways, so I cannot see why this wouldn't work...
Is there a different way to do this? Should I just avoid two (or more) levels of template indirection?
Well, consider this:
template <>
struct my_common_type<int> {
constexpr static const bool valid = true;
using type = my_type_a;
};
template <>
struct my_common_type<double> {
constexpr static const bool valid = true;
using type = my_type_a;
};
// ...
int main(void) {
my_function(my_type_a{}, 1.0);
}
Does the compiler chooses my_common_type<int> or my_common_type<double>?
If the language would permit deduction in you case, it would have to match what T would be in my_common_type<T>::type in order to yield the exact type you send to the function parameter. Obviously, it's not only impossible, but with my example above, it may have multiple choices!
Fortunately, there is a way to tell the compiler that my_common_type<T> will always yield to T. The basics of the trick is this:
template<typename T>
using test_t = T;
template<typename T>
void call(test_t<T>) {}
int main() {
call(1);
}
What is T deduces to? int, easy! The compiler is happy with this kind of match. Also, since test_t cannot be specialized, test_t<soxething> is known to only be something.
Also, this is working too with multiple levels of aliases:
template<typename T>
using test_t = T;
template<typename T>
using test2_t = test_t<T>;
template<typename T>
void call(test2_t<T>) {}
int main() {
call(1); // will also work
}
We can apply this to your case, but we will need some tool:
template<typename T, typename...>
using first_t = T;
This is the same easy match as above, but we can also send some argument that will not be used. We will make sfinae in this unused pack.
Now, rewrite my_common_type_t to still be an easy match, whilst adding the constraint in the unused pack:
template <typename T>
using my_common_type_t = first_t<T, typename my_common_type<T>::type>;
Note that this is also working:
template <typename T>
using my_common_type_t = first_t<T, std::enable_if_t<my_common_type<T>::valid>>;
Now deduction will happen as expected! Live (GCC) Live (Clang)
Note that this trick will only work with C++14, as sfinae in this case (dropped parameters) is only guaranteed to happen since C++14.
Also note that you should either use struct for your trait, or use public: to make the member my_common_type<T>::type public, or else GCC will output a bogus error.