Using SFINAE to detect a static constexpr [duplicate] - templates

I'm working on upgrading some C++ code to take advantage of the new functionality in C++11. I have a trait class with a few functions returning fundamental types which would most of the time, but not always, return a constant expression. I would like to do different things based on whether the function is constexpr or not. I came up with the following approach:
template<typename Trait>
struct test
{
template<int Value = Trait::f()>
static std::true_type do_call(int){ return std::true_type(); }
static std::false_type do_call(...){ return std::false_type(); }
static bool call(){ return do_call(0); }
};
struct trait
{
static int f(){ return 15; }
};
struct ctrait
{
static constexpr int f(){ return 20; }
};
int main()
{
std::cout << "regular: " << test<trait>::call() << std::endl;
std::cout << "constexpr: " << test<ctrait>::call() << std::endl;
}
The extra int/... parameter is there so that if both functions are available after SFINAE, the first one gets chosen by overloading resolution.
Compiling and running this with Clang 3.2 shows:
regular: 0
constexpr: 1
So this appears to work, but I would like to know if the code is legal C++11. Specially since it's my understanding that the rules for SFINAE have changed.

NOTE: I opened a question here about whether OPs code is actually valid. My rewritten example below will work in any case.
but I would like to know if the code is legal C++11
It is, although the default template argument may be considered a bit unusual. I personally like the following style better, which is similar to how you (read: I) write a trait to check for a function's existence, just using a non-type template parameter and leaving out the decltype:
#include <type_traits>
namespace detail{
template<int> struct sfinae_true : std::true_type{};
template<class T>
sfinae_true<(T::f(), 0)> check(int);
template<class>
std::false_type check(...);
} // detail::
template<class T>
struct has_constexpr_f : decltype(detail::check<T>(0)){};
Live example.
Explanation time~
Your original code works† because a default template argument's point of instantiation is the point of instantiation of its function template, meaning, in your case, in main, so it can't be substituted earlier than that.
§14.6.4.1 [temp.point] p2
If a function template [...] is called in a way which uses the definition of a default argument of that function template [...], the point of instantiation of the default argument is the point of instantiation of the function template [...].
After that, it's just usual SFINAE rules.
† Atleast I think so, it's not entirely clear in the standard.

Prompted by #marshall-clow, I put together a somewhat more-generic version of an type-trait for detecting constexpr. I modelled it on std::invoke_result, but because constexpr depends on the inputs, the template arguments are for the values passed in, rather than the types.
It's somewhat limited, as the template args can only be a limited set of types, and they're all const when they get to the method call. You can easily test a constexpr wrapper method if you need other types, or non-const lvalues for a reference parameter.
So somewhat more of an exercise and demonstration than actually-useful code.
And the use of template<auto F, auto... Args> makes it C++17-only, needing gcc 7 or clang 4. MSVC 14.10.25017 can't compile it.
namespace constexpr_traits {
namespace detail {
// Call the provided method with the provided args.
// This gives us a non-type template parameter for void-returning F.
// This wouldn't be needed if "auto = F(Args...)" was a valid template
// parameter for void-returning F.
template<auto F, auto... Args>
constexpr void* constexpr_caller() {
F(Args...);
return nullptr;
}
// Takes a parameter with elipsis conversion, so will never be selected
// when another viable overload is present
template<auto F, auto... Args>
constexpr bool is_constexpr(...) { return false; }
// Fails substitution if constexpr_caller<F, Args...>() can't be
// called in constexpr context
template<auto F, auto... Args, auto = constexpr_caller<F, Args...>()>
constexpr bool is_constexpr(int) { return true; }
}
template<auto F, auto... Args>
struct invoke_constexpr : std::bool_constant<detail::is_constexpr<F, Args...>(0)> {};
}
Live demo with use-cases on wandbox

Related

Why does decltype return type fail for recursive template, while return type deduction works just fine?

While working on a C++11 type-set, I tried to implement this function (stripped down to the minimum):
constexpr auto test() -> bool;
template <typename T, typename... Rest>
constexpr auto test() -> decltype(test<Rest...>())
{
return {};
}
Both gcc and clang choke on this. Clang says:
test.cpp:54:40: error: 'Rest' does not refer to a value
constexpr auto test() -> decltype(test<Rest...>())
^
gcc complains:
test.cpp:54:44: error: expected primary-expression before ‘...’ token
constexpr auto test() -> decltype(test<Rest...>())
I guess this is because the variadic version of test is not even fully declared when the decltype looks at it.
However, when I use return type deduction in C++14, this compiles just fine:
constexpr auto test() -> bool;
template <typename T, typename... Rest>
constexpr auto test()
{
return test<Rest...>();
}
Seems like test is considered sufficiently declared here.
I wonder why this doesn't work for the decltype variant? Even if I turn on C++14 support?
PS: It turns out, that I cannot really call even the C++14 function, so maybe the whole thing is botched...
One problem is that your first test overload is not a function template, so can't be called with test<>() syntax.
However, decltype doesn't really work with recursive variadic functions like that, since the return type is part of the declaration of the function, so that overload is not declared when the name is looked up.
You can get around this in C++11 by using template classes instead:
template <typename... Ts>
struct test;
template <typename T, typename... Ts>
struct test<T,Ts...> {
static decltype(test<Ts...>::value()) value() { return test<Ts...>::value(); }
};
template <>
struct test<> {
static bool value() { return true; }
};
Live demo
In C++14 you can make the first overload take a single template parameter and the second take two and a parameter pack:
template <typename T>
constexpr auto test() -> bool { return true; }
template <typename T, typename U, typename... Rest>
constexpr auto test()
{
return test<U,Rest...>();
}
Live demo
While the template function itself is being declared, the template function itself has not been declared. So it is not visible to the trailing return type decltype.
You can fix this with ADL. If your template function takes an argument from the same namespace as your template function, the lookup for the return type becomes willing to look at the template function itself. This is because templates do lookup for their return type signature using both the context just before they where declared, and using ADL on each of the parameters.
template<class...Ts> struct types {};
namespace bob{
struct mytag{};
constexpr auto test(mytag, types<>) -> bool{ return true; }
template <typename T, typename... Rest>
constexpr auto test(mytag, types<T,Rest...>)
-> decltype( test( mytag{}, types<Rest...>{} ) )
{
return test(mytag{},types<Rest...>{});
}
}
template<class...Ts>
constexpr auto test()
->decltype( bob::test( bob::mytag{}, types<Ts...>{} ) ){
return bob::test( bob::mytag{}, types<Ts...>{} );
}
You may need constexpr types(){}; and similar for mytag depending on compiler.
This also fixes the fact that test<> is illegal in your original code. Functions do much better with types passed by (tag template wrapped) value in my experience. Overloading is more friendly.

Why Can't These Templatized Functions Take No Arguments?

I am trying to use a couple templatized functions for Substitution Fail Is Not An Error(SFINAE). And I can do it like this:
template<typename R, typename S = decltype(declval<R>().test())> static true_type Test(R*);
template<typename R> static false_type Test(...);
But I'm not understanding how the argument makes this SNFIAE work. It seems like I should just be able to remove the arguments and the template selection would work the exact same way:
template<typename R, typename S = decltype(declval<R>().test())> static true_type Test();
template<typename R> static false_type Test();
But it does not, I get:
Call of overloaded 'Test()' is ambiguous
What is it about these arguments that make this SFINAE work?
Your second example fails to compile, since there are two overloads of Test with identical signature, becasue defaulted template type arguments are not part of function signature. That is not allowed.
Your first example works in followign manner:
When type R does have a function test in it, both Test become valid overload candidates. However, ellipsis functions have lower rank than non-ellipsis ones, and thus compiler selects the first overload, returning true_type.
When R does not have test on it, the first overload is excluded from overload resolution set (SFINAE at works). You are left with only the second one, which returns false_type.
The question has been answered but maybe it's useful to go into a deeper explanation.
Hopefully this annotated program will make things clearer:
#include <utility>
#include <iostream>
// define the template function Test<R> if and only if the expression
// std::declval<R>().test()
// is a valid expression.
// in which case Test<R, decltype(std::declval<R>().test())>(0) will be preferrable to... (see after)
template<typename R, typename S = decltype(std::declval<R>().test())>
static std::true_type Test(R*);
// ...the template function Test<R>(...)
// because any function overload with specific arguments is preferred to this
template<typename R> static std::false_type Test(...);
struct foo
{
void test();
};
struct bar
{
// no test() method
// void test();
};
// has_test<T> deduces the type that would have been returned from Test<T ... with possibly defaulted args here>(0)
// The actual Test<T>(0) will be the best candidate available
// For foo, it's Test<foo, decltype(std::declval<R>().test())>(foo*)
// which deduces to
// Test<foo, void>(foo*)
// because that's a better match than Test<foo>(...)
//
// for bar it's Test<bar>(...)
// because Test<bar, /*error deducing type*/>(bar*)
// is discarded as a candidate, due to SFNAE
//
template<class T>
constexpr bool has_test = decltype(Test<T>(0))::value;
int main()
{
std::cout << has_test<foo> << std::endl;
std::cout << has_test<bar> << std::endl;
}

Deducing a function pointer return type

I think code will better illustrate my need:
template <typename F>
struct return_type
{
typedef ??? type;
};
so that:
return_type<int(*)()>::type -> int
return_type<void(*)(int,int)>::type -> void
I know of decltype and result_of but they need to have arguments passed. I want to deduce the return type of a function pointer from a single template parameter. I cannot add the return type as a parameter, because that's exactly what I want to hide here...
I know there's a solution in boost, but I can't use it, and an attempt to dig it out from boost resulted in a spectacular failure (as it often does).
C++11 solutions welcome (as long as supported in VS2012).
If you can use variadic templates (November '12 CTP), this should work:
template <class F>
struct return_type;
template <class R, class... A>
struct return_type<R (*)(A...)>
{
typedef R type;
};
Live example.
If you can't use variadic templates, you'll have to provide specific specialisations for 0, 1, 2, ... parameters (by hand or preprocessor-generated).
EDIT
As pointed out in the comments, if you want to work with variadic functions as well, you'll have to add one extra partial specialisation (or one for each parameter count in the no-variadic-templates case):
template <class R, class... A>
struct return_type<R (*)(A..., ...)>
{
typedef R type;
};
It has been a while since the question has been asked. For C++17, there is an interesting option. However, the syntax is a bit different from what was originally asked, but the result (a type) is the same.
First, we need a helper function. As you see here, the function accepts a function pointer and returns an object of type R. We need this only for a decltype statement and therefore this function will never be called, so a forward declaration is sufficient.
template<typename R, typename... ARGS>
static R return_type(R (*)(ARGS...)); // forward declaration only for decltype
The trick is to provide the function pointer as a template auto parameter, which is forwarded to a decltype statement:
template<auto FUNCTION_POINTER>
using ReturnType = decltype(return_type(FUNCTION_POINTER));
Now it is easy to get the return type:
#include <iostream>
#include <type_traits>
template<typename R, typename... ARGS>
static R return_type(R (*)(ARGS...)); // forward declaration only for decltype
template<auto FUNCTION_POINTER>
using ReturnType = decltype(return_type(FUNCTION_POINTER));
int func1(char c, int i, long l); // also here only forward declarations needed
void func2(unsigned u, float f);
double func3(bool b);
int main()
{
std::cout << std::is_same_v<int, ReturnType<func1>> << std::endl;
std::cout << std::is_same_v<void, ReturnType<func2>> << std::endl;
std::cout << std::is_same_v<double, ReturnType<func3>> << std::endl;
std::cout << std::is_same_v<void, ReturnType<func1>> << std::endl;
}
You can try the complete example in Wandbox: https://wandbox.org/permlink/5akL0MQDoDuJlQNY

Detecting constexpr with SFINAE

I'm working on upgrading some C++ code to take advantage of the new functionality in C++11. I have a trait class with a few functions returning fundamental types which would most of the time, but not always, return a constant expression. I would like to do different things based on whether the function is constexpr or not. I came up with the following approach:
template<typename Trait>
struct test
{
template<int Value = Trait::f()>
static std::true_type do_call(int){ return std::true_type(); }
static std::false_type do_call(...){ return std::false_type(); }
static bool call(){ return do_call(0); }
};
struct trait
{
static int f(){ return 15; }
};
struct ctrait
{
static constexpr int f(){ return 20; }
};
int main()
{
std::cout << "regular: " << test<trait>::call() << std::endl;
std::cout << "constexpr: " << test<ctrait>::call() << std::endl;
}
The extra int/... parameter is there so that if both functions are available after SFINAE, the first one gets chosen by overloading resolution.
Compiling and running this with Clang 3.2 shows:
regular: 0
constexpr: 1
So this appears to work, but I would like to know if the code is legal C++11. Specially since it's my understanding that the rules for SFINAE have changed.
NOTE: I opened a question here about whether OPs code is actually valid. My rewritten example below will work in any case.
but I would like to know if the code is legal C++11
It is, although the default template argument may be considered a bit unusual. I personally like the following style better, which is similar to how you (read: I) write a trait to check for a function's existence, just using a non-type template parameter and leaving out the decltype:
#include <type_traits>
namespace detail{
template<int> struct sfinae_true : std::true_type{};
template<class T>
sfinae_true<(T::f(), 0)> check(int);
template<class>
std::false_type check(...);
} // detail::
template<class T>
struct has_constexpr_f : decltype(detail::check<T>(0)){};
Live example.
Explanation time~
Your original code works† because a default template argument's point of instantiation is the point of instantiation of its function template, meaning, in your case, in main, so it can't be substituted earlier than that.
§14.6.4.1 [temp.point] p2
If a function template [...] is called in a way which uses the definition of a default argument of that function template [...], the point of instantiation of the default argument is the point of instantiation of the function template [...].
After that, it's just usual SFINAE rules.
† Atleast I think so, it's not entirely clear in the standard.
Prompted by #marshall-clow, I put together a somewhat more-generic version of an type-trait for detecting constexpr. I modelled it on std::invoke_result, but because constexpr depends on the inputs, the template arguments are for the values passed in, rather than the types.
It's somewhat limited, as the template args can only be a limited set of types, and they're all const when they get to the method call. You can easily test a constexpr wrapper method if you need other types, or non-const lvalues for a reference parameter.
So somewhat more of an exercise and demonstration than actually-useful code.
And the use of template<auto F, auto... Args> makes it C++17-only, needing gcc 7 or clang 4. MSVC 14.10.25017 can't compile it.
namespace constexpr_traits {
namespace detail {
// Call the provided method with the provided args.
// This gives us a non-type template parameter for void-returning F.
// This wouldn't be needed if "auto = F(Args...)" was a valid template
// parameter for void-returning F.
template<auto F, auto... Args>
constexpr void* constexpr_caller() {
F(Args...);
return nullptr;
}
// Takes a parameter with elipsis conversion, so will never be selected
// when another viable overload is present
template<auto F, auto... Args>
constexpr bool is_constexpr(...) { return false; }
// Fails substitution if constexpr_caller<F, Args...>() can't be
// called in constexpr context
template<auto F, auto... Args, auto = constexpr_caller<F, Args...>()>
constexpr bool is_constexpr(int) { return true; }
}
template<auto F, auto... Args>
struct invoke_constexpr : std::bool_constant<detail::is_constexpr<F, Args...>(0)> {};
}
Live demo with use-cases on wandbox

Why should I avoid std::enable_if in function signatures

Scott Meyers posted content and status of his next book EC++11.
He wrote that one item in the book could be "Avoid std::enable_if in function signatures".
std::enable_if can be used as a function argument, as a return type or as a class template or function template parameter to conditionally remove functions or classes from overload resolution.
In this question all three solution are shown.
As function parameter:
template<typename T>
struct Check1
{
template<typename U = T>
U read(typename std::enable_if<
std::is_same<U, int>::value >::type* = 0) { return 42; }
template<typename U = T>
U read(typename std::enable_if<
std::is_same<U, double>::value >::type* = 0) { return 3.14; }
};
As template parameter:
template<typename T>
struct Check2
{
template<typename U = T, typename std::enable_if<
std::is_same<U, int>::value, int>::type = 0>
U read() { return 42; }
template<typename U = T, typename std::enable_if<
std::is_same<U, double>::value, int>::type = 0>
U read() { return 3.14; }
};
As return type:
template<typename T>
struct Check3
{
template<typename U = T>
typename std::enable_if<std::is_same<U, int>::value, U>::type read() {
return 42;
}
template<typename U = T>
typename std::enable_if<std::is_same<U, double>::value, U>::type read() {
return 3.14;
}
};
Which solution should be preferred and why should I avoid others?
In which cases "Avoid std::enable_if in function signatures" concerns usage as return type (which is not part of normal function signature but of template specializations)?
Are there any differences for member and non-member function templates?
Put the hack in the template parameters.
The enable_if on template parameter approach has at least two advantages over the others:
readability: the enable_if use and the return/argument types are not merged together into one messy chunk of typename disambiguators and nested type accesses; even though the clutter of the disambiguator and nested type can be mitigated with alias templates, that would still merge two unrelated things together. The enable_if use is related to the template parameters not to the return types. Having them in the template parameters means they are closer to what matters;
universal applicability: constructors don't have return types, and some operators cannot have extra arguments, so neither of the other two options can be applied everywhere. Putting enable_if in a template parameter works everywhere since you can only use SFINAE on templates anyway.
For me, the readability aspect is the big motivating factor in this choice.
std::enable_if relies on the "Substition Failure Is Not An Error" (aka SFINAE) principle during template argument deduction. This is a very fragile language feature and you need to be very careful to get it right.
if your condition inside the enable_if contains a nested template or type definition (hint: look for :: tokens), then the resolution of these nested tempatles or types are usually a non-deduced context. Any substitution failure on such a non-deduced context is an error.
the various conditions in multiple enable_if overloads cannot have any overlap because overload resolution would be ambiguous. This is something that you as an author need to check yourself, although you'd get good compiler warnings.
enable_if manipulates the set of viable functions during overload resolution which can have surprising interactions depending on the presence of other functions that are brought in from other scopes (e.g. through ADL). This makes it not very robust.
In short, when it works it works, but when it doesn't it can be very hard to debug. A very good alternative is to use tag dispatching, i.e. to delegate to an implementation function (usually in a detail namespace or in a helper class) that receives a dummy argument based on the same compile-time condition that you use in the enable_if.
template<typename T>
T fun(T arg)
{
return detail::fun(arg, typename some_template_trait<T>::type() );
}
namespace detail {
template<typename T>
fun(T arg, std::false_type /* dummy */) { }
template<typename T>
fun(T arg, std::true_type /* dummy */) {}
}
Tag dispatching does not manipulate the overload set, but helps you select exactly the function you want by providing the proper arguments through a compile-time expression (e.g. in a type trait). In my experience, this is much easier to debug and get right. If you are an aspiring library writer of sophisticated type traits, you might need enable_if somehow, but for most regular use of compile-time conditions it's not recommended.
Which solution should be preferred and why should I avoid others?
Option 1: enable_if in the template parameter
It is usable in Constructors.
It is usable in user-defined conversion operator.
It requires C++11 or later.
In my opinion, it is the more readable (pre-C++20).
It is easy to misuse and produce errors with overloads:
template<typename T, typename = std::enable_if_t<std::is_same<T, int>::value>>
void f() {/*...*/}
template<typename T, typename = std::enable_if_t<std::is_same<T, float>::value>>
void f() {/*...*/} // Redefinition: both are just template<typename, typename> f()
Notice the use of typename = std::enable_if_t<cond> instead of the correct std::enable_if_t<cond, int>::type = 0
Option 2 : enable_if in the return type
It cannot be used with constructors (which have no return type)
It cannot be used in user-defined conversion operator (as it is not deducible)
It can be used pre-C++11.
Second more readable IMO (pre-C++20).
Option 3: enable_if in a function parameter
It can be use pre-C++11.
It is usable in Constructors.
It cannot be used in user-defined conversion operators (they have no parameters)
It cannot be used in methods with a fixed number of arguments, such as unary/binary operators +, -, * and others.
It is safe for use in inheritance (see below).
Changes function signature (you have basically an extra as last argument void* = nullptr); this causes pointers pointers to the function to behave differently and so on.
Option4 (C++20) requires
There is now requires clauses
It is usable in Constructors
It is usable in user-defined conversion operator.
It requires C++20
IMO, the most readable
It is safe to use with inheritance (see below).
Can use directly template parameter of the class
template <typename T>
struct Check4
{
T read() requires(std::is_same<T, int>::value) { return 42; }
T read() requires(std::is_same<T, double>::value) { return 3.14; }
};
Are there any differences for member and non-member function templates?
There are subtle differences with inheritance and using:
According to the using-declarator (emphasis mine):
namespace.udecl
The set of declarations introduced by the using-declarator is found by performing qualified name lookup ([basic.lookup.qual], [class.member.lookup]) for the name in the using-declarator, excluding functions that are hidden as described below.
...
When a using-declarator brings declarations from a base class into a derived class, member functions and member function templates in the derived class override and/or hide member functions and member function templates with the same name, parameter-type-list, cv-qualification, and ref-qualifier (if any) in a base class (rather than conflicting). Such hidden or overridden declarations are excluded from the set of declarations introduced by the using-declarator.
So for both template argument and return type, methods are hidden is following scenario:
struct Base
{
template <std::size_t I, std::enable_if_t<I == 0>* = nullptr>
void f() {}
template <std::size_t I>
std::enable_if_t<I == 0> g() {}
};
struct S : Base
{
using Base::f; // Useless, f<0> is still hidden
using Base::g; // Useless, g<0> is still hidden
template <std::size_t I, std::enable_if_t<I == 1>* = nullptr>
void f() {}
template <std::size_t I>
std::enable_if_t<I == 1> g() {}
};
Demo (gcc wrongly finds the base function).
Whereas with argument, similar scenario works:
struct Base
{
template <std::size_t I>
void h(std::enable_if_t<I == 0>* = nullptr) {}
};
struct S : Base
{
using Base::h; // Base::h<0> is visible
template <std::size_t I>
void h(std::enable_if_t<I == 1>* = nullptr) {}
};
Demo
and with requires too:
struct Base
{
template <std::size_t I>
void f() requires(I == 0) { std::cout << "Base f 0\n";}
};
struct S : Base
{
using Base::f;
template <std::size_t I>
void f() requires(I == 1) {}
};
Demo
"Which solution should be preferred and why should I avoid others?"
When the question was asked, std::enable_if from <type_traits> was the best tool available, and the other answers are reasonable up to C++17.
Nowadays in C++20 we have direct compiler support via requires.
#include <concepts
template<typename T>
struct Check20
{
template<typename U = T>
U read() requires std::same_as <U, int>
{ return 42; }
template<typename U = T>
U read() requires std::same_as <U, double>
{ return 3.14; }
};