pack expansion used as argument for non-pack parameter of concept - c++

template<typename T> concept A = true;
template<typename... T> concept B = A<T...>;
Clang and gcc complains 'pack expansion used as argument for non-pack parameter of concept' at the 2nd line. MSVC compiles. Are the code well-formed or not?
Here is a more concrete example showing why the question matters. Say we want to define a concept with parameter pack called single_integral_or_all_floating_point. It is true if the pack has single integral type or all floating point types. It is false otherwise.
template<typename... T>
concept single_integral_or_all_floating_point =
std::is_integral<T...>::value ||
(... && std::is_floating_point<T>::value);
If sizeof...(T) is not 1, there is a substitution failure on std::is_integral and the 1st constraint is not satisfied.
Let's say we want to get rid of the old type traits and switch to concepts. Clang and gcc do not compile with a drop-in replacement like below because of 'pack expansion used as argument for non-pack parameter of concept'. MSVC compiles.
template<typename... T>
concept single_integral_or_all_floating_point =
std::integral<T...> || // error: pack expansion used as argument for non-pack parameter of concept
(... && std::floating_point<T>);
We have to come up a workaround for clang/gcc.
template<typename... T>
struct only {};
template<typename T>
struct only<T> {
using type = T;
};
template<typename... T>
concept single_integral_or_all_floating_point =
std::integral<typename only<T...>::type> ||
(... && std::floating_point<T>);
So if clang and gcc are correct, it is pretty awkward to use concepts in the similar cases.

The main issue with your first snippet is that concepts must be able to be normalized, and normalization requires a parameter mapping of each template parameter of an atomic constraint. The fact that the prototype parameter of A is not used in its constraint seems to be irrelevant to GCC/Clang; they apparently attempt to create some kind of temporary mapping anyway, which fails since a non-pack cannot map to a pack. Which makes sense IMO, because that would be incompatible in whatever internal system normal forms are expressed in.
Regarding your broader use case, there are certainly more sensible ways to express your constraint, e.g. explicitly checking sizeof.... You could also use a variable template, whose template-id would be an atomic constraint and thus not prone to the same problem:
template<typename... T>
concept single_integral_or_all_floating_point =
std::is_integral_v<T...> ||
(... && std::floating_point<T>);
Demo.

Related

C++Concepts: How to enforce a method to accept a constrained type [duplicate]

I'm trying to write a C++20 concept to express the requirement that a type have a certain method, which takes an argument, but for the purposes of this concept I don't care what the argument type is.
I've tried to write something like:
template <typename T>
concept HasFooMethod = requires(T t, auto x)
{
{ t.Foo(x) } -> std::same_as<void>;
};
however, both gcc and clang reject this, giving an error that 'auto' cannot be used in the parameter list of a requires expression this way.
An alternative would be to put the type of 'x' as a second template parameter:
template <typename T, typename TX>
concept HasFooMethod = requires(T t, TX x)
{
{ t.Foo(x) } -> std::same_as<void>;
};
but then this requires TX to be specified explicitly whenever the concept is used, it cannot be deduced:
struct S { void Foo(int); };
static_assert(HasFooMethod<S>); // doesn't compile
static_assert(HasFooMethod<S, int>); // the 'int' must be specified
Is there any way to write a concept that allows Foo to take an argument of unspecified type?
The question Concept definition requiring a constrained template member function is very similar, but not the same: that question asks how to require that a (templated) method can take any type satisfying a given concept, while this question is about requiring that a method takes some particular type, although that type is unspecified. In terms of quantifiers, the other question is asking about (bounded) universal quantification while this one is about existential quantification. The other question's answer also does not apply to my case.
Concepts are not intended to provide the kind of functionality you are looking for. So they don't provide it.
A concept is meant to constrain templates, to specify a set of expressions or statements that a template intends to use (or at least be free to use) in its definition.
Within the template that you are so constraining, if you write the expression t.Foo(x), then you know the type of x. It is either a concrete type, a template parameter, or a name derived from a template parameter. Either way, the type of x is available at the template being constrained.
So if you want to constrain such a template, you use both the type of t and the type of x. Both are available to you at that time, so there is no problem with creating such a constraint. That is, the constraint is not on T as an isolated type; it's on the association between T and X.
Concepts aren't meant to work in a vacuum, devoid of any association with the actual place of usage of the constraint. You shouldn't focus on creating unary concepts so that users can static_assert their classes against them. Concepts aren't meant for testing if a type fulfills them (which is basically what your static_assert is doing); they're meant for constraining the template definition that uses them.
Your constraint needs to be FooCallableWith, not HasFooMethod.
Something close to this can be accomplished by defining an adapter type that can implicitly convert to (almost) anything:
struct anything
{
// having both these conversions allows Foo's argument to be either
// a value, an lvalue reference, or an rvalue reference
template <typename T>
operator T&();
template <typename T>
operator T&&();
};
Note that these operators do not need to be implemented, as they will only be used in an unevaluated context (and indeed, they could not be implemented for all types T).
Then, HasFooMethod can be written as:
template <typename T>
concept HasFooMethod = requires(T t, anything a)
{
{ t.Foo(a) } -> std::same_as<void>;
};

How can unspecified types be used in C++20 'requires' expressions?

I'm trying to write a C++20 concept to express the requirement that a type have a certain method, which takes an argument, but for the purposes of this concept I don't care what the argument type is.
I've tried to write something like:
template <typename T>
concept HasFooMethod = requires(T t, auto x)
{
{ t.Foo(x) } -> std::same_as<void>;
};
however, both gcc and clang reject this, giving an error that 'auto' cannot be used in the parameter list of a requires expression this way.
An alternative would be to put the type of 'x' as a second template parameter:
template <typename T, typename TX>
concept HasFooMethod = requires(T t, TX x)
{
{ t.Foo(x) } -> std::same_as<void>;
};
but then this requires TX to be specified explicitly whenever the concept is used, it cannot be deduced:
struct S { void Foo(int); };
static_assert(HasFooMethod<S>); // doesn't compile
static_assert(HasFooMethod<S, int>); // the 'int' must be specified
Is there any way to write a concept that allows Foo to take an argument of unspecified type?
The question Concept definition requiring a constrained template member function is very similar, but not the same: that question asks how to require that a (templated) method can take any type satisfying a given concept, while this question is about requiring that a method takes some particular type, although that type is unspecified. In terms of quantifiers, the other question is asking about (bounded) universal quantification while this one is about existential quantification. The other question's answer also does not apply to my case.
Concepts are not intended to provide the kind of functionality you are looking for. So they don't provide it.
A concept is meant to constrain templates, to specify a set of expressions or statements that a template intends to use (or at least be free to use) in its definition.
Within the template that you are so constraining, if you write the expression t.Foo(x), then you know the type of x. It is either a concrete type, a template parameter, or a name derived from a template parameter. Either way, the type of x is available at the template being constrained.
So if you want to constrain such a template, you use both the type of t and the type of x. Both are available to you at that time, so there is no problem with creating such a constraint. That is, the constraint is not on T as an isolated type; it's on the association between T and X.
Concepts aren't meant to work in a vacuum, devoid of any association with the actual place of usage of the constraint. You shouldn't focus on creating unary concepts so that users can static_assert their classes against them. Concepts aren't meant for testing if a type fulfills them (which is basically what your static_assert is doing); they're meant for constraining the template definition that uses them.
Your constraint needs to be FooCallableWith, not HasFooMethod.
Something close to this can be accomplished by defining an adapter type that can implicitly convert to (almost) anything:
struct anything
{
// having both these conversions allows Foo's argument to be either
// a value, an lvalue reference, or an rvalue reference
template <typename T>
operator T&();
template <typename T>
operator T&&();
};
Note that these operators do not need to be implemented, as they will only be used in an unevaluated context (and indeed, they could not be implemented for all types T).
Then, HasFooMethod can be written as:
template <typename T>
concept HasFooMethod = requires(T t, anything a)
{
{ t.Foo(a) } -> std::same_as<void>;
};

Why does enable_if<>* = nullptr work when enable_if<> = void doesn't?

Basic Problem Statement
I'm learning about SFINAE. I tried an extremely simple enable_if:
// 1: A foo() that accepts arguments that are derived from Base
template <typename T, typename Enable = enable_if_t<std::is_base_of_v<Base, T>>>
void foo(T thing) {
std::cout << "It's a derived!" << std::endl;
}
// 2: A foo() that accepts all other arguments
template <typename T, typename Enable = enable_if_t<!std::is_base_of_v<Base, T>>>
void foo(T thing) {
std::cout << "It's not a derived." << std::endl;
}
The compiler complains that foo is multiply defined. The internet tells me that this is because the template arguments aren't relevant when checking function signatures.
Variants I Tried
In my quest to do the most basic metaprogramming possible, I began throwing syntax at the problem. Here's a list of things that I tried for the enable_if statement (inverted statement i.e. !std::is_base_of identical, but omitted for brevity):
Anonymous Type, No typename, Equals 0
https://en.cppreference.com/w/cpp/types/enable_if tells me that what I did above was wrong. But its suggestion (found under the first notes block) is appropriately cryptic, and more importantly, also doesn't compile.
std::enable_if_t<std::is_base_of_v<Base, T>> = 0
Anonymous Type, No typename, Equals void
Thinking that maybe if I'm programming with types, using a type would be a wise choice, I instead tried to default the template to void. No dice.
std::enable_if_t<std::is_base_of_v<Base, T>> = void
Anonymous Type, Yes typename, Equals void
While we're throwing syntax at it, if I'm defaulting this template parameter to a type, shouldn't I use the typename keyword?
typename std::enable_if_t<std::is_base_of_v<Base, T>> = void
What Finally And Oh So Obviously Worked
typename enable_if_t<std::is_base_of_v<Base, T>, T>* = nullptr
I've asked everyone I know why this works yet my other variants don't, and they are equally confused. I'm at a loss. To make matters more confusing, if I name this type (e.g. typename Enable = ...), it fails to compile.
I would be extremely grateful if one who is more familiar with TMP and enable_if would explain to me:
Why does declaring the enable_if as a pointer to a type and defaulting it to nullptr work?
What are the semantic rules for defaulting enable_if?
What are the semantic rules for naming types produced by enable_if?
Is there a reference I can use which clearly explains this and other rules like it in template-land?
Many thanks.
The first set of variants you are just setting the value of a template type argument. Two overloads with different values for a template type argument collide, as they are both of kind template<class,class> and have the same function arguments.
The non-type template argument cases, the ones where you use a raw enable if you end up having a template non type argument of type void. That is illegal; the various error messages are the various ways it is illegal.
When you add a star, when the enable if clause passes it is a template non type argument of type void pointer.
When it fails, it isn't an argument at all.
An equivalent to the nullptr case is:
std::enable_if_t<std::is_base_of_v<Base, T>, bool> = true
when the clause is true, the enable if evaluates to bool, and we get:
bool = true
a template non-type argument of type bool that defaults to true. When the clause (the base of clause) is false, we get a SFINAE failure; there is no template type or non-type argument there.
With the class Whatever = enable_if cases we are trying SFINAE based on default value of template arguments. This leads to signature collision, because signatures have to be unique if they are found during overload resolution (in the same phase).
With the enable = value cases, we are trying SFINAE based on if there is a template non-type argument there. On failure, there is no signature to compare, so it cannot collide.
What remains is to make the syntax simple and pretty.
Now, this is all obsolete with Concepts, so don't fall in love with the syntax.

Short-circuiting template specialization in std::disjunction

Some Background:
I'm working on putting together a templated class which, as part of template specialization, deduces a type to use for one of its members. This data member needs to support being streamed over the wire, and I'm trying to keep the system as flexible and extensible as possible (with the goal that new variants of the type can be created by modifying some high-level elements of the specialization logic without getting into the guts of the implementation code). Some of the existing usages specialize this data member to be an enum, and the streaming code supports converting this value back and forth to a 32-bit integer for transmission over the wire.
Because an enum could be defined (either implicitly or explicitly) to be backed by a different type -- most dangerous in the case being a 64-bit value -- I'd like to be able enforce that if the resolved type is an enum, its underlying type must be a 32-bit integer (more generally, I just need to enforce that it's a maximum of 32 bits, but I'll be worrying about that complexity once the simpler case is working).
My Attempted Solution:
Stitching together some of the tools provided by type_traits, I came up with the following:
#include <cstdint>
#include <type_traits>
using TestedType = /* Logic for deducing a type */;
constexpr bool nonEnumOrIntBacked =
!std::is_enum_v<TestedType>
|| std::is_same_v<std::underlying_type_t<TestedType>, std::int32_t>;
static_assert(nonEnumOrIntBacked,
"TestedType is an enum backed by something other than a 32-bit integer");
However, when I tried to compile this (using Visual Studio 2017 on the latest update), I was met with the error text
'TestedType': only enumeration type is allowed as an argument to compiler intrinsic type trait '__underlying_type'. Seeing this, I tried an alternative formulation using std::disjunction, which I believe should short-circuit evaluation of templates if an earlier condition evaluates to true (I've ommitted the std qualifiers to make this a bit more readable):
disjunction_v<
negation<is_enum<TestedType>>,
is_same<underlying_type_t<TestedType>, int32_t>
>;
I've also tried wrapping the offending usage of underlying_type_t inside an enable_if predicated on the type being an enum, but didn't have success with that either.
My Question:
Do boolean operators in general (and std::disjunction in particular) not short-circuit evaluation of templates? On the cppreference page for std::disjunction, it states the following (emphasis mine):
Disjunction is short-circuiting: if there is a template type argument Bi with bool(Bi::value) != false, then instantiating disjunction::value does not require the instantiation of Bj::value for j > i
Reading this, I would have expected the ill-formed nature of underlying_type_t<TestedType> for some non-enum type to be irrelevant, since downstream types don't need to be considered once something upstream has been evaluated as true.
If my reading of the spec is incorrect on this point, is there another way to accomplish this check at compile-time, or will I need to add a runtime check to enforce this?
Template arguments are "evaluated" eagerly just like regular arguments. The part causing problems is underyling_type_t, but it's present in full in both versions. You need to delay that part until after you know TestedType is an enum type. This is fairly straightforward with constexpr if (live example):
template<typename T>
constexpr bool nonEnumOrIntBackedImpl() {
if constexpr (std::is_enum_v<T>) {
return std::is_same_v<std::underlying_type_t<T>, std::int32_t>;
} else {
return false;
}
}
template<typename T>
constexpr bool nonEnumOrIntBacked = nonEnumOrIntBackedImpl<T>();
Prior to C++17, one common method is tag dispatching (live example):
template<typename T>
constexpr bool nonEnumOrIntBackedImpl(std::true_type) {
return std::is_same<std::underlying_type_t<T>, std::int32_t>{};
}
template<typename T>
constexpr bool nonEnumOrIntBackedImpl(std::false_type) {
return false;
}
template<typename T>
constexpr bool nonEnumOrIntBacked = nonEnumOrIntBackedImpl<T>(std::is_enum<T>{});
chris explained why the construct failed and gave a solution. But in the spirit of exploiting every single feature of the library, here's how to use the short-circuitry
template<typename T, typename I>
struct is_underlying
{
static constexpr auto value =
std::is_same_v<std::underlying_type_t<T>, I>;
};
using TestedType = int;
constexpr bool nonEnumOrIntBacked =
std::disjunction_v<std::negation<std::is_enum<TestedType>>,
is_underlying<TestedType, std::int32_t>>;
The template needs to be well-formed, but not its value.
An alternative:
template <typename T> struct identity { using type = T; };
bool nonEnumOrIntBacked =
std::is_same<
std::conditional_t<
std::is_enum_v<TestedType>,
std::underlying_type<TestedType>,
identity<void>>::type,
int
>::value
;
with conditional<cont, type1, type2>::type::type :) to delay evaluation.

Is it possible to infer template parameters of tuple from brace-type initialization?

In this example, is it possible to allow the deduction of the template parameters type of the tuple?
#include<tuple>
#include<string>
template<class T1, class T2>
void fun(std::tuple<T1, T2> t, std::string other){}
int main(){
fun(std::tuple<double, int>(2.,3), std::string("other")); // ok
fun(std::make_tuple(2.,3), std::string("other")); // ok, but trying to avoid `make_tuple`
fun({2.,3},std::string("other")); // desired syntax but
// giving compilation error: candidate template ignored: couldn't infer template argument 'T1' void fun(std::tuple<T1, T2> t)
}
I added the second argument other to avoid solutions involving variadic arguments at the level of the function fun. Also, I am trying to avoid the use of make_tuple, at least from the user code (i.e. in main()). In fact it doesn't need to be the tuple type the one involved as long as the "desired syntax" is allowed and somehow its element types can be deduced at later stage.
(Also, although similar, this has nothing to do with initializer_list since it doesn't work at all having dissimilar elements in the braces)
It fails at least with clang 3.2 and gcc 4.7.2. Is there any hope that it would work with the current or a near-future standard? (e.g. a future(?) initializer_tuple.)
(This can be very useful to add expressiveness to function calls, by aggregating subelements, but that can be argued about)
Note: For the example code it seems that std::forward_as_tuple is more appropriate than std::make_tuple so the arguments are not necessarily copied: http://en.cppreference.com/w/cpp/utility/tuple/forward_as_tuple . Still not as nice as if there were a build-in language feature for heterogeneous initializer lists.
No, there is absolutely no way. Deduction fails if the element types are not of the same type. And no deduction is done at all if the parameter is not a std::initializer_list<T> anyway (you are right that initializer_list doesn't have anything to do with the braces you give, but this is the simple rule for deduction to work).
The template parameter values must be deduced by other function parameter positions involving them or must be explicitly specified.
It is the age of C++20 and there is still no general way to do this.
Workaround 1:
There is a partial solution in the case that the bracket syntax it is, for any reason, reserved for a particular combination of arguments: for example
template<class T1 = double, class T2 = int>
void fun(std::tuple<T1, T2> t, std::string other){}
int main(){
fun({2.,3},std::string("other")); // now works at least, but it may not mean what it seems
}
https://godbolt.org/z/xGaEP5EGn
The worst part of this solution is that it lets exchange the double and the int, but I would say that is a problem of the implicit conversions of built-in types.
It works for any version of C++.
Workaround 2:
The only change since the OP is that now there is CTAD (class template argument deduction).
Which means that there is a new partial workaround for this problem, which is
fun(std::tuple{2.,3}, std::string("other")); // ok, but still trying to avoid `tuple`
Fantasy solution:
This inspired me to think that may be this can be done if CTAD is extended in the future with one of these syntax:
The natural extension to CTAD is if this worked:
void fun(std::tuple t, std::string other){} // implicitly a template because std::tuple is a template
More explicitly:
void fun(std::tuple<auto, auto> t, std::string other){} // implicitly a template because std::tuple is a template and `auto`.
or
template<template<typename...> class Tuple = std::tuple>
void fun(Tuple t, std::string other){}
or
template<template<typename...> class Tuple = std::tuple, class T1, class T2>
void fun(Tuple<T1, T2> t, std::string other){}
None of these work currently, but I am fond of the first and the last option.
The first because it is an extension of CTAD (the type is moved from the calling point to the function argument).
Besides CTAD has been shown to work with (non deduced) template template classes.
The last because it is template argument deduction, under some expanded definition of deduction.
The default argument can help deduce the template template argument (std::tuple).