Is the following code valid? If it depends on the C++ version, I would also be interested in the corresponding differences (starting from C++14).
#include <type_traits>
struct S {
template< typename ... Args >
void foo() const {
static_assert( sizeof...(Args) == 0, "" );
}
template< typename ... Args, bool B = false, typename std::enable_if<B, bool>::type = false >
void foo() {
static_assert( sizeof...(Args) == 0, "" );
}
};
int main() {
S s{};
s.foo();
}
I am asking because of the following things in the cpp standard (I do not know whether this is the actual version):
The validity of a template may be checked prior to any instantiation.
The program is ill-formed, no diagnostic required, if
no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, ...
My goal: I am trying to remove the non-const version from overload resolution, when some macros are defined during compilation.
Related
I was really excited when I first heard about C++20 constraints and concepts, and so far I've been having a lot of fun testing them out. Recently, I wanted to see if it's possible to use C++20 concepts to test the constraints of classes or functions. For example:
template <int N>
requires (N > 0)
class MyArray { ... };
template <int N>
concept my_array_compiles = requires {
typename MyArray<N>;
};
my_array_compiles<1>; // true
my_array_compiles<0>; // false
At first I didn't have any issues, but I encountered a case where static_assert in a dependent function prevents compilation, even though it appears in a requires expression. Here is an example that illustrates this:
template <bool b>
requires b
struct TestA {
void foo() {}
};
template <bool b>
struct TestB {
static_assert(b);
void foo() {}
};
template <template<bool> class T, bool b>
concept can_foo = requires (T<b> test) {
test.foo();
};
can_foo<TestA, true>; // true
can_foo<TestA, false>; // false
can_foo<TestB, true>; // true
// can_foo<TestB, false>; does not compile
TestA and TestB should work similarly for most use cases (although I found that TestB<false> can be used as a type as long as it isn't instantiated or dereferenced). However, my expectation was that a failed static_assert within a requires expression would cause it to evaluate to false instead. This is especially important for using library code that still uses static_assert. For example, std::tuple_element:
template <class T>
concept has_element_0 = requires {
typename tuple_element_t<0, T>;
};
has_element_0<tuple<int>>; // true
// has_element_0<tuple<>>; does not compile
When I pass in an empty tuple to the above concept, I get the error static_assert failed due to requirement '0UL < sizeof...(_Types)' "tuple_element index out of range". I've tested this on g++ 10.3.0 and clang 12.0.5. I was able to work around this issue by providing a wrapper that uses constraints, but it somewhat defeats the purpose since I am essentially preventing the compiler from seeing the static_assert by enforcing the same condition at a higher level.
template <size_t I, class T>
requires (I >= 0) && (I < tuple_size_v<T>)
using Type = tuple_element_t<I, T>;
template <class T>
concept has_element_0 = requires {
typename Type<0, T>;
};
has_element_0<tuple<int>>; // true
has_element_0<tuple<>>; // false
And it doesn't always work depending on how std::tuple_element is used:
template <size_t I, class T>
requires (I >= 0) && (I < tuple_size_v<T>)
tuple_element_t<I, T> myGet(const T& tup) {
return get<I>(tup);
}
template <class T>
concept has_element_0 = requires (T tup) {
myGet<0>(tup);
};
has_element_0<tuple<int>>; // true
// has_element_0<tuple<>>; does not compile
So ultimately my questions are: is this expected behavior that requires expressions don't take static_assert into account? If so, what was the reason for that design? And finally, is there a better way to accomplish my goal on classes with static_assert without using the above workaround?
Thanks for reading.
Yes, nothing in the content of the stuff you interact with is checked. Just the immediate context of the declaration.
In some cases with decltype the non immediate context of some constructs is checked, but any errors remain hard.
This was done (way back) to reduce the requirements on compilers. Only in what is known as "immediate context" do the compilers need to be able to cleanly back out when they see an error and continue compiling.
Static assert is never suitable for this purpose. Static assert, if hit, ends the compilation.
If you want to avoid the static assert (that is expected to end compilation) then you need to provide an alternative.
Once the concept is designed, create a variant for the not (!) of that concept:
#include <tuple>
#include <variant>
template <std::size_t I, class T>
requires (I >= 0) && (I < std::tuple_size_v<T>)
using Type = std::tuple_element_t<I, T>;
template <class T>
concept has_element_0 = requires {
typename Type<0, T>;
};
bool test1()
{
return has_element_0<std::tuple<int>>; // true
}
bool test2()
{
return has_element_0<std::tuple<>>; // false
}
template <std::size_t I, class T>
requires (I >= 0) && (I < std::tuple_size_v<T>)
std::tuple_element_t<I, T> myGet_impl(const T& tup) {
return get<I>(tup);
}
template <class T>
concept alt_has_element_0 = requires (T tup) {
myGet_impl<0>(tup);
};
template <class T>
auto myGet0();
template <class T>
requires (alt_has_element_0<T>)
auto myGet0(const T& tup)
{
return myGet_impl<0, T>(tup);
}
auto test3()
{
std::tuple<int> X{7};
return myGet0(X); // true
}
template <class T>
requires (!alt_has_element_0<T>)
auto myGet0(const T& tup)
{
return std::monostate{};
}
auto test4()
{
std::tuple<> X;
return myGet0(X); // true
}
see it here;
Notice for test4() to compile, the code above defiles what to do if we do not fulfill the requirements of the concept. I stole std::monostate from variant for this.
Edit: This funcion have to check types one by one and return obj of any that will satisfy condition or nullptr.
template <typename... Args, typename = std::enable_if_t<(sizeof...(Args) == 0)>()>
std::nullptr_t f() { return nullptr; }
template <typename T, typename... Args>
BaseClassOfAllArgs* f() {
if (<some condition related to T...>)
return new T;
return f<Args...>();
}
This code works for me. But I wonder if is it possible to rewrite this code to use concept?
I mean something like this:
template <typename... Args>
concept NoArgs = (sizeof...(Args) == 0);
and then use it instead of std::enable_if(this code doesnt work)
template <NoArgs Args>
std::nullptr_t f() { return nullptr; }
template <typename T, typename... Args>
BaseClassOfAllArgs* f() {
if (<some condition related to T...>)
return new T;
return f<Args...>();
}
EDIT: Here is working example of code after taking some tips from
guys in comments. After I added 'Base' to the template it turns out that EmptyPack concept is no longer needed. And First template naturally needs 3 typenames. However, I am not sure about this concept EmptyPack. Is it really making my program ill-formed no diagnostic required?
#include <iostream>
#include <type_traits>
#include <typeinfo>
class X {};
class A : public X {};
class B : public X {};
class C : public X {};
class D : public C {};
class E {};
template<class T, class... Args >
concept DerivedsOfBase = (std::is_base_of_v<T, Args> && ...);
template<typename... Args>
concept EmptyPack = sizeof...(Args) == 0;
template<typename T>
std::nullptr_t f() {
std::cout << "End of the types list" << std::endl;
return nullptr;
}
template<typename Base, typename T, typename... Args> requires DerivedsOfBase<Base, T, Args...>
Base* f() {
std::cout << typeid(T).name() << std::endl;
if (<some condition related to T>)
return new T;
return f<Base, Args...>();
}
int main()
{
auto ptr = f<X, A, B, C>();
auto ptr2 = f<X, A, B, D>();
//auto ptr3 = f<X, A, B, E>(); // compile error
return 0;
}
Any template parameter pack that only has valid instantiations for packs of size 0 makes your program ill-formed, no diagnostic required.
This applies to your first "working" example, as well as any practical variant I can think of using concepts.
From the N3690 draft standard [temp.res] 14.6/8:
If every valid specialization of a variadic template requires an empty template parameter pack, the template is ill-formed, no diagnostic required.
(I've seen that in many versions of C++, I'm just using a random draft standard as it showed up when I googled C++ standard pdf.)
Note that (a) your two f are overloads not specializations, and (b) what C++ programmers mean by "valid specialization of a template" and what the standard means are not quite the same thing.
In essence, pretty much any attempt at your concept will result in ill-formed, no diagnostic required programs.
To see the problem here, we'll reword the standard using negation:
If (every valid specialization of a variadic template requires an empty template parameter pack) then (the template is ill-formed, no diagnostic required).
We can convert this "forall" into a "there exists". "Forall X, P(X)" is the same as "Not( There exists X, Not P(X) )".
Unless (there exists a valid specialization of a variadic template with a non-empty template parameter pack) then (the template is ill-formed, no diagnostic required).
If a variadic template has a requires clause that mandates the variadic template pack is empty, then no valid specialization of that template with an empty parameter pack exists. So your template is ill-formed, no diagnostic required.
In general, these kind of rules exist so that compilers can check if your code would always be nonsense. Things like templates where no type parameter could make it compile, or packs that must be empty, are generally a sign that your code has bugs. By making it ill-formed and not requiring a diagnostic, compilers are permitted to emit diagnostics and fail to compile.
An issue I have with the standard is that it is not just permitted to fail to compile, but it is permitted to compile a program that does literally anything.
Because the compiler is permitted to do this, and some optimizations in other cases actually result in this happening, you should avoid writing code that is ill-formed, no diagnostic required, like the plague.
A workaround is:
namespace implementation_details {
struct never_use_me;
}
template <class=implementation_details::never_use_me>
std::nullptr_t f() { return nullptr; }
template <typename T, typename... Args>
T* f() {
if (<some condition related to T...>)
return new T;
return f<Args...>();
}
another option is:
template <typename T, typename... Args>
T* f() {
if (<some condition related to T...>)
return new T;
if constexpr (sizeof...(Args)==0)
return nullptr;
else
return f<Args...>();
}
You don't really need concepts for that, simply use if constexpr:
T* f() {
if constexpr (sizeof...(Args) != 0) {
if (<some condition related to T...>)
return new T;
return f<Args...>();
} else {
return nullptr;
}
}
If you really want separated functions, you could always use overloading:
template <typename T> // no pack
T* f() {
return nullptr;
}
template <typename T, typename Arg1, typename... Args> // one of more Args
T* f() {
if (<some condition related to T...>)
return new T;
return f<Arg1, Args...>();
}
But of course you can always use requires expressions if you really want to keep the same logic you had with SFINAE. It should be possible (and enough) without a declared concept, but only with constraint:
template <typename T, typename...> // pack logically always empty
T* f() {
return nullptr;
}
template <typename T, typename... Args> requires (sizeof...(Args) > 0)
T* f() {
if (<some condition related to T...>)
return new T;
return f<Args...>();
}
Of course you can also wrap the condition inside a concept, but you gain very little using this technique in this case, since you still have to use the concept inside a require clause:
template<typename... Args>
concept nonempty_pack = sizeof...(Args) > 0;
template <typename T, typename...> // pack logically always empty
T* f() {
return nullptr;
}
template <typename T, typename... Args> requires nonempty_pack<Args...>
T* f() {
if (<some condition related to T...>)
return new T;
return f<Args...>();
}
The syntax template<my_concept T> will send T as the first parameter. You'll always get that parameter snet automatically, hence the need to put the concept in the requires clause.
This question was inspired by this answer. I wonder what are/were the best ways to simplify it in given standards. One I know and personally used/still use since C++14 is macro REQUIRES(x):
With definition:
template<long N>
struct requires_enum
{
enum class type
{
none,
all
};
};
#define REQUIRES(...) requires_enum<__LINE__>::type = \
requires_enum<__LINE__>::type::none, \
bool PrivateBool = true, \
typename std::enable_if<PrivateBool && (__VA_ARGS__), int>::type = 0
And use if even for non-templated function calls:
template<REQUIRES(sizeof(int)==4)>
int fun() {return 0;}
int main()
{
fun(); //only if sizeof(int)==4
}
The original REQUIRES I use is from this post.
What are the other good techniques?
Some examples of SFINAE that require some, or long time to understand for reader that just started the adventure with SFINAE:
Pre-C++11 SFINAE example (Source):
template <typename T>
struct has_typedef_foobar {
// Types "yes" and "no" are guaranteed to have different sizes,
// specifically sizeof(yes) == 1 and sizeof(no) == 2.
typedef char yes[1];
typedef char no[2];
template <typename C>
static yes& test(typename C::foobar*);
template <typename>
static no& test(...);
// If the "sizeof" of the result of calling test<T>(nullptr) is equal to sizeof(yes),
// the first overload worked and T has a nested type named foobar.
static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
};
If you are working with C++11 (the example code contains std::enable_if, so I guess this is the case) or a successive revision, I would use a static_assert in this case:
int fun() {
static_assert(sizeof(int)==4, "!");
return 0;
}
int main() {
fun();
}
You don't have a set of functions from which to pick a working one up.
As I've been said once, this is more a substitution failure is always an error than a substitution failure is not an error.
What you want is a compile-time trigger and a static_assert does it with gentle error messages.
Of course, it's also far easier to read than a complicated sfinae expression too!!
If you want to choose between two functions and you don't want to use template machinery or macros, do not forget that overloading is part of the language (pre-C++11 working example):
#include <iostream>
template<bool> struct tag {};
int fun(tag<true>) { return 0; }
int fun(tag<false>) { return 1; }
int fun() { return fun(tag<sizeof(int) == 4>()); }
int main() {
std::cout << fun() << std::endl;
}
This can be easily extended to the cases where the functions are more than two:
#include <iostream>
template<int> struct tag {};
int fun(tag<0>) { return 0; }
int fun(tag<1>) { return 1; }
int fun(tag<2>) { return 2; }
int fun(bool b) {
if(b) { return fun(tag<0>()); }
else { return fun(tag<(sizeof(int) == 4) ? 1 : 2>());
}
int main() {
std::cout << fun(false) << std::endl;
}
You can put those functions in an anonymous namespace and get away with them.
Of course, note also that in pre-C++11 we were authorized to write enable_if and all the other things from type_traits for ourselves.
As an example:
template<bool b, typename = void>
struct enable_if { };
template<typename T>
struct enable_if<true, T> { typedef T type; };
Enable if is pretty easy to implement. Take a look at this implementation:
template<bool b, typename T = void>
struct enable_if {
typedef T type;
};
template<typename T>
struct enable_if<false, T> {};
In C++11 I usually declare some aliases. Since you're stuck in pre C++11 era, you can do that instead:
template<bool b>
struct enable_if_parameter : enable_if<b, int*> {};
Then you can use the struct like this:
template<typename T, typename enable_if_parameter<(sizeof(T) >= 0)>::type = 0>
void someFunc() {
// ...
}
If you can allow yourself some C++17, you could do that:
template<bool b>
using enable_if_parameter = std::enable_if_t<b, int*>;
And then do that:
template<typename T, enable_if_parameter<std::is_same_v<T, int>> = 0>
I also love the void_t idom to create new type traits:
template<typename T, typename = void>
struct has_callme : std::false_type {};
template<typename T>
struct has_callme<T, void_t<decltype(std::declval<T>().callme())>> : std::true_type {};
In C++03 you simply write enable_if yourself. It requires no C++11 features.
The reason why you use different techniques is that the pre-C++11 compilers sometimes have a funny definition of what SFINAE and what should be an error. MSVC is the current major compiler who still (in the pre-C++17 era) has very quirky definition of what is valid SFINAE due to their "SFINAE decltype" issues.
In C++11 you should write void_t and enable_if_t to simplify your SFINAE stuff.
You should also write this:
namespace details {
template<template<class...>class Z, class always_void, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z, void, Ts...>;
which lets you write traits and ask if something is valid easily (can you call a method? Create an alias that does a decltype on the invocation, then ask if you can apply the type to the alias). This is still needed in C++14 and 17, but C++20 probably will get is_detected which serves a similar purpose.
So can_print is:
template<class T>using print_result = decltype(
std::declval<std::ostream&>() << std::declval<T>()
);
template<class T>using can_print = can_apply< print_result, T >;
it is either truthy or falsy depending on if << works on a stream with it.
In C++14 you can start using hana-style metaprogramming to create lambdas that do type manipulation for you. In C++17 they become constexpr, which gets rid of some issues.
Using techniques like the OP's macro tends to lead to ill formed programs, no diagnostic required. This is because if a template has no valid template parameters that would lead to the body of the template being valid code, your program is ill formed, no diagnostic required.
So this:
template<REQUIRES(sizeof(int)==4)>
int fun() {
// code that is ill-formed if `int` does not have size 4
}
will most likely compile and run and "do what you want", but it is actually an ill-formed program when sizeof(int) is 8.
The same may be true of using this technique to disable methods on classes based on the template arguments of the class. The stanard is unclear on the issue, so I avoid it.
The REQUIRES macro tries to hide how it works behind magic, but it is far too easy to step over the line and generate an ill-formed program. Hiding the magic when the details of the magic cause your code to be ill-formed is not a good plan.
Tag dispatching can be used to simplify otherwise complex SFINAE issues. It can be used to order overloads or pick between them, or pass more than one bundle of types to a helper template function.
template<std::size_t N>
struct overload_priority : overload_priority<N-1> {};
template<>
struct overload_priority<0> {};
Now you can pass overload_priority<50>{} to a set of functions, and the one with the highest overload_priority<?> in that slot will be preferred.
template<class T>struct tag_t{using type=T;};
namespace details {
inline int fun( tag_t<int[4]> ) { return 0; }
inline int fun( tag_t<int[8]> ) { return 1; }
}
int fun() { return details::fun( tag_t<int[sizeof(int)]>{} ); }
just dispatched to a different function depending on the size of int.
Both fun overloads get compiled and checked, so you don't run into the stealth ill-formed program problem.
A function whose validity is not a function of its template arguments is not safe to use in C++. You must use a different tactic. Machinery that makes this easier to do just makes it easier to write ill-formed programs.
I have a scoped enum:
enum class E
{ A, B, C };
Now I want to have a function, which accepts a value of that scoped int OR an int itself.
It should be something like:
template <typename T, std::enable_if_t<std::is_same<T, enum E **OR** int>::value, int> = 0 >
void foo(T value);
But I don't know how to deal with the concept of OR in C++ templates. I know std::is_convertible, however I don't even know, if I could use it here, because you can only static_cast scoped enums to int.
But anyway I don't want to accept any type that is convertible to int but only types of that single enum or int.
Overload seems the simplest:
void foo(int value);
void foo(E value) { foo(static_cast<int>(value); } // Or specific code
template <typename T> void foo(T) = delete; // To forbid type convertible to int
else you can use SFINAE
template <typename T>
std::enable_if_t<std::is_same<int, T>::value || std::is_same<E, T>::value>
foo(T value);
Because std::is_same<...>::value is a boolean, you can simply use || operator with 2 std::is_same<...>::value :
template <typename T, std::enable_if_t<std::is_same<T, enum E>::value || std::is_same<T, int>::value, int> = 0 >
void foo(T value);
std::is_same instantiations define a constexpr implicit bool conversion, so you can instantiate them and perform logical OR with ||. In C++17, you will also be able to use std::disjunction to similar effect, although this will likely compile slower for only two type traits. Example of both:
#include <type_traits>
enum class E
{ A, B, C };
template <typename T, std::enable_if_t<
std::is_same<T, E>{} || std::is_same<T, int>{},
int> = 0>
void foo(T){
}
//in C++17, you can also do this:
template <typename T, std::enable_if_t<
std::disjunction<std::is_same<T, E>, std::is_same<T, int>>{},
int> = 0>
void bar(T){
}
int main() {
foo(E::A);
foo(0);
//foo('A'); fails
bar(E::A);
bar(0);
//bar('A'); fails
return 0;
}
std::disjunction is the logical OR template you were wondering about (although I recommend using || with std::is_same for your case). Interestingly, std::disjunction even performs logical short-circuiting of template instantiations, just like the humble old || operator does in a runtime context. I believe recent versions of libc++ are already shipping with std::disjunction. If your <type_traits> implementation doesn't have it yet, the example implementation at cppreference works just fine for me. If you get the chance, you should see how it works. It's rather clever!
I am trying to get static_assert to help me avoid null pointers in C++11.
The problem seems to be that C++11 require the compiler to compile templates even if they are not instantiated.
I have the following code:
#include <type_traits>
template<typename T, typename... Us>
std::enable_if_t< std::is_constructible<T, Us...>::value == true, T * >
create_if_constructible(Us... args) { return new T(args...); }
template<typename T, typename... Us>
std::enable_if_t< std::is_constructible<T, Us...>::value == false, T * >
create_if_constructible(Us... args) {
static_assert( false, "Class T constructor does not match argument list.");
return nullptr;
}
struct ClassA {
ClassA(int a, string b) {}
};
void foo() {
ClassA *a = create_if_constructible<ClassA>(1, "Hello");
// ClassA *b = create_if_constructible<ClassA>(1, "Hello", "world"); // I want compile time error here.
}
I would like this to compile without error. But the static_assert is compiled and gives me a compile time error.
Only if the the second instantiation of the ClassA is in the code should it give me a compile time error.
The standard permits, but does not require, compilers to diagnose templates for which no valid instantiation can be generated. This can range from simple syntax errors to your example of a constant false expression in a static_assert. ยง14.6 [temp.res]/p8:
If no valid specialization can be generated for a template, and that
template is not instantiated, the template is ill-formed, no
diagnostic required.
I'm rather baffled by all this SFINAE machinery, though. A simple
template<typename T, typename... Us>
T* create_if_constructible(Us... args) { return new T(args...); }
already refuses to compile if T is not constructible from the parameter given, so I'm not sure how this complex circumlocution will help you "avoid null pointers".
Regardless, a simple way to make choosing the second function template a compile-time error is to explicitly delete it.
template<typename T, typename... Us>
std::enable_if_t< std::is_constructible<T, Us...>::value == false, T * >
create_if_constructible(Us... args) = delete;
Alternatively, if you are partial to static_asserts, perhaps because of the custom error message, you must ensure that there is theoretically a way to generate a valid instantiation of your template. That means that 1) what you are static_asserting on must depend on a template argument, and 2) there must be theoretically a way for the condition to be true. A simple way is to use an auxiliary template:
template<class> class always_false : std::false_type {};
template<typename T, typename... Us>
std::enable_if_t< std::is_constructible<T, Us...>::value == false, T * >
create_if_constructible(Us... args) {
static_assert( always_false<T>::value, "Class T constructor does not match argument list.");
return nullptr;
}
The key point here is that the compiler cannot assume that always_false<T>::value is always false because it is always possible that there's a specialization later that sets it to true, and so it is not allowed to reject this at template definition time.
In all C++ standards ever, templates are compiled in two phases. The second phase is instantiation, but compilation can also fail in phase 1. In particular, syntax errors are detected in phase 1.
In your case, the simpler solution is to leave out the body of the second instantiation.
Another solution is to use T in the static_assert, so the compiler must delay evaluation to phase 2. Trivially: static_assert(sizeof(T)==0,