When you are working with templates and with decltype you often need an instance of a certain type even though you do not have any the moment. In this case, std::declval<T>() is incredibly useful. This creates an imaginary instance of the type T.
Is there a something similar for concepts? i.e. a function which would create and imaginary type for a concept.
Let me give you an example(a bit contrived but should serve the purpose):
Let's define a concept Incrementable
template <typename T>
concept Incrementable = requires(T t){
{ ++t } -> T;
};
Now I would like to have a concept which tests if an object has the operator operator() which can accept Incrementable. In my imaginary syntax I would write something like this:
template <typename F, typename T = declval<Incrementable>>
concept OperatesOnIncrementable = requires(F f, T t){
{ f(t) } -> T;
}
There the declval in typename T = declval<Incrementable> would create an imaginary type T which is not really a concrete type but for all intents and purposes behaves like a type which satisfies Incrementable.
Is there a mechanism in the upcoming standard to allow for this? I would find this incredibly useful.
Edit: Some time ago I have asked a similar question if this can be done with boost::hana.
Edit: Why is this useful? For example if you want to write a function which composes two functions
template <typename F, typename G>
auto compose(F f, G g) {
return [f, g](Incrementable auto x) { return f(g(x)); };
}
I want to get an error when I try to compose two functions which cannot be composed. Without constraining the types F and G I get error only when I try to call the composed function.
There is no such mechanism.
Nor does this appear to be implementable/useful, since there is an unbounded number of Incrementable types, and F could reject a subset selected using an arbitrarily complex metaprogram. Thus, even if you could magically synthesize some unique type, you still have no guarantee that F operates on all Incrementable types.
Is there a something similar for concepts? i.e. a function which would create and imaginary type for a concept.
The term for this is an archetype. Coming up with archetypes would be a very valuable feature, and is critical for doing things like definition checking. From T.C.'s answer:
Thus, even if you could magically synthesize some unique type, you still have no guarantee that F operates on all Incrementable types.
The way to do that would be to synthesize an archetype that as minimally as possible meets the criteria of the concept. As he says, there is no archetype generation in C++20, and is seems impossible given the current incarnation of concepts.
Coming up with the correct archetype is incredibly difficult. For example, for
template <typename T>
concept Incrementable = requires(T t){
{ ++t } -> T;
};
It is tempting to write:
struct Incrementable_archetype {
Incrementable_archetype operator++();
};
But that is not "as minimal as possible" - this type is default constructible and copyable (not requirements that Incrementable imposes), and its operator++ returns exactly T, which is also not the requirement. So a really hardcore archetype would look like:
struct X {
X() = delete;
X(X const&) = delete;
X& operator=(X const&) = delete;
template <typename T> operator,(T&&) = delete;
struct Y {
operator X() &&;
};
Y operator++();
};
If your function works for X, then it probably works for all Incrementable types. If your function doesn't work for X, then you probably need to either change the implementation so it does or change the constraints to to allow more functionality.
For more, check out the Boost Concept Check Library which is quite old, but is very interesting to at least read the documentation.
Related
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>;
};
The syntax that works for classes does not work for concepts:
template <class Type>
concept C = requires(Type t) {
// ...
};
template <class Type>
concept C<Type*> = requires(Type t) {
// ...
};
MSVC says for the line of the "specialization": error C7606: 'C': concept cannot be explicitly instantiated, explicitly specialized or partially specialized.
Why cannot concepts be specialized? Is there a theoretical reason?
Because it would ruin constraint normalization and subsumption rules.
As it stands now, every concept has exactly and only one definition. As such, the relationships between concepts are known and fixed. Consider the following:
template<typename T>
concept A = atomic_constraint_a<T>;
template<typename T>
concept B = atomic_constraint_a<T> && atomic_constraint_b<T>;
By C++20's current rules, B subsumes A. This is because, after constraint normalization, B includes all of the atomic constraints of A.
If we allow specialization of concepts, then the relationship between B and A now depends on the arguments supplied to those concepts. B<T> might subsume A<T> for some Ts but not other Ts.
But that's not how we use concepts. If I'm trying to write a template that is "more constrained" than another template, the only way to do that is to use a known, well-defined set of concepts. And those definitions cannot depend on the parameters to those concepts.
The compiler ought to be able to compute whether one constrained template is more constrained than another without having any template arguments at all. This is important, as having one template be "more constrained" than another is a key feature of using concepts and constraints.
Ironically, allowing specialization for concepts would break (constrained) specialization for other templates. Or at the very least, it'd make it really hard to implement.
In addition to the great answer from Nicol Bolas:
Concepts are a bit special, because they don't behave like other templated things:
13.7.9 Concept definitions
(5) A concept is not instantiated ([temp.spec]).
[Note 1: A concept-id ([temp.names]) is evaluated as an expression. A concept cannot be explicitly instantiated ([temp.explicit]), explicitly specialized ([temp.expl.spec]), or partially specialized ([temp.spec.partial]). — end note]
Due to concepts not being able to be instantiated they also can't be specialized.
I'm not sure on why the standard decided to not make them specializable, given that it's easy to emulate specializations.
While you can't specialize concepts directly, there are quite a few ways you can work around the problem.
You can use any type of constant expression in a concept - so you could use a templated variable (which can be specialized) and just wrap it up into a concept - the standard does this for quite a few of its own concepts as well, e.g. std::is_intergral:
template<class T> struct is_integral;
// is_integral is specialized for integral types to have value == true
// and all others are value == false
template<class T>
inline constexpr bool is_integral_v = is_integral<T>::value;
template<class T>
concept integral = is_integral_v<T>;
So you could easily write a concept that has specializations like this: godbolt example
struct Foo{};
struct Bar{};
template<class T>
constexpr inline bool is_addable_v = requires(T t) {
{ t + t } -> std::same_as<T>;
};
// Specializations (could also use other requires clauses here)
template<>
constexpr inline bool is_addable_v<Foo> = true;
template<class T>
constexpr inline bool is_addable_v<T&&> = true;
template<class T>
concept is_addable = is_addable_v<T>;
int main() {
static_assert(is_addable<int>);
static_assert(is_addable<Foo>);
static_assert(!is_addable<Bar>);
static_assert(is_addable<Bar&&>);
}
Or by using a class:
template<class T>
struct is_addable_v : std::true_type {};
template<>
struct is_addable_v<struct FooBar> : std::false_type {};
template<class T>
concept is_addable = is_addable_v<T>::value;
Or even a constexpr lambda: godbolt example
// pointers must add to int
// everything else must add to double
template<class T>
concept is_special_addable = ([](){
if constexpr(std::is_pointer_v<T>)
return requires(std::remove_pointer_t<T> t) {
{ t + t } -> std::same_as<int>;
};
else
return requires(T t) {
{ t + t } -> std::same_as<double>;
};
})();
int main() {
static_assert(is_special_addable<double>);
static_assert(is_special_addable<int*>);
static_assert(!is_special_addable<double*>);
static_assert(!is_special_addable<int>);
}
So while concepts can't be specialized on their own, it's easy to achieve the same effect with existing language features.
Specialization in this sort of situation opens up a bag of worms. We opened this bag up once with template specialization. Template specialization is a major part of what makes the template language in general Turing complete. Yes, you can program in templates. You shouldn't, but you can. Boost has a library called Boost.MPL that's chock full of clever things, like an "unordered map" that operates at compile time, rather than run time.
So we would have to restrict it carefully. Simple cases may work, but complex cases would have to be forbidden. Certainly anything that is remotely capable of creating a recursive constraint would have to be watched carefully. Indeed, consider a concept:
template <typename T>
concept hailstone = false;
template <int i>
concept hailstone<std::integral_constant<int, i> =
hailstone<2 * i> || (i % 2 == 1 && hailstone<3*i - 1>);
template <>
concept hailstone<std::integral_constant<int, 0> = true;
so, is std::integral_constant<int, 27> a hailstone? It could take a while. My chosen example is based on hailstone numbers from the Collatz Conjecture. Determining whether any given number is a hailstone or not is painfully difficult (even though, as best as we can tell, every number is a hailstone number).
Now replace integral_constant with a clever structure which can do arbitrary precision. Now we're in trouble!
Now we can carefully slice off elements of this problem and mark them as doable. The spec community is not in that business. The Concepts we know in C++20 has been nicknamed concepts-lite because it's actually a drastically simplified version of a concepts library that never made it into C++11. That library effectively implemented a Description Logic, a class of logic that is known to be decidable. This was important because the computer had to run through all of the necessary calculations, and we didn't want them to take an infinite amount of time. Concepts is derived from this, so it follows the same rules. And, if you look in Description Logics, the way you prove many statements involves first enumerating the list of all named concepts. Once you had enumerated that, it was trivial to show that you could resolve any concept requirement in finite time.
As Nicol Bolas points out in his answer, the purpose of concepts was not to be some clever Turing complete system. It was to provide better error messages. Thus, while one might be able to cleverly slide in some specialization within carefully selected paths, there's no incentive to.
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>;
};
The last time I used C++ concepts with GCC and the fconcepts flag the following snippet used to work
template <typename T, typename U>
concept equality_comparable = requires(T a, U b) {
{ a == b } -> bool;
{ a != b } -> bool;
};
Apparently this is no longer the case and a return-type-requirement after a compound requirement can now only contain type constraints. If I'm not mistaken this basically means using another concept to satisfy the return-type-requirement.
So the perfectly readable and (for C++ standards) short snippet becomes
template <typename From, typename To>
concept convertible_to = std::is_convertible_v<From, To>;
template <typename T, typename U>
concept equality_comparable = requires(T a, U b) {
{ a == b } -> convertible_to<bool>;
{ a != b } -> convertible_to<bool>;
};
Of course this isn't even a full implementation, but let's ignore that for now. Could someone maybe explain to me why the committee decided to change that? Personally I find that "implicitly used template parameter" in the convertible_to concept extremely irritating and confusing.
Well, what does this actually mean:
template <typename T, typename U>
concept equality_comparable = requires(T a, U b) {
{ a == b } -> bool;
{ a != b } -> bool;
};
Does it mean a == b must have type exactly bool, or does it mean if you decay the type you get bool (i.e. const bool or bool& are ok), or does it mean convertible to bool (i.e. std::true_type is ok)? I don't think it's at all clear from the syntax - and any one of these three could be meaningfully desired by a particular concept (as P1452 points out, at the time, the ratio of Same<T> to ConvertibleTo<T> in concepts was 40-14).
The paper also goes on to point out that in the Concepts TS, where -> Type existed, we also had the ability to write something like vector<Concept>... or -> vector<Concept> as a requirement. That's a type, but would behave very difficultly with the decltype(()) semantics we adopted in P1084.
Basically I don't think the "perfectly readable" snippet actually is - there are multiple potential meanings for that syntax, all of which can be the desired meaning depending on context. And the most commonly used one at the time (same_as<bool>) isn't even the one we want here (convertible_to<bool>).
Personally I find that "implicitly used template parameter" in the convertible_to concept extremely irritating and confusing.
It's novel in C++, but I personally find it reads quite nicely in these cases. Seeing:
{ a == b } -> convertible_to<bool>;
Just reads exactly as the requirement: a == b needs to be a valid expression that's convertible to bool. For unary concepts, it makes the usage quite nice since you can use them in place of the somewhat meaningless typename/class keyword:
template <range R>
void algo(R&& r);
Which isn't that different from other languages. Like, in Rust for instance:
fn algo<I: Iterator>(i: I)
There the "implicitly used template parameter" is so implicit that it's not even part of the trait declaration, it's implicit there too:
pub trait Iterator { ... }
So even with a longer-form syntax, you'd write where I: Iterator whereas in C++ you'd still write requires range<R>.
This isn't strictly related to the original question, but I just find it interesting to add for some other color.
There is a common abstraction for both containers and functions. I learned it in Haskell, and I'm trying to implement it in C++.
Most C++ programmers are familiar with std::transform, roughly speaking given a function from type A to B, you can convert a container of type A to a container of type B.
You can transform functions in a similar way, given a function foo from A to B, you can convert a function bar taking Z to A to a function foo . bar taking Z to B. The implementation is simple, it's just composition.
I wanted to define a function fmap, on containers and functions, to reflect this abstraction for generic programming.
The container was easy (I know this isn't fully general)
template <typename A, typename Func>
auto fmap(Func f, vector<A> in) {
vector<decltype(f(in[0]))> out_terms{};
for(auto vec : in)
out_terms.push_back(f(vec));
return out_terms;
}
However, the analogous function for functions makes me much more nervous.
template <typename FuncT, typename Func>
auto fmap(FuncT f, Func in) {
return [f, in](auto x){
return f(in(x));
};
}
Although the template won't specialize for anything except callable things, I'm worried this will confuse overload resolution. I would like to introduce type constraints on the template parameters to restrict their resolution to function types to keep the name space clean. And I was going to ask how to do that.
This abstraction is extremely general, there are corresponding fmaps for pointers to values, which I suspect might conflict as well.
So I think my question is, can I have two different template implementations with the same template level signature? I'm almost certain the answer is no but maybe something similar can be faked. And if not, what tools are available today to distinguish between the overloads? Especially for function types.
This seems, to me, to be a textbook case for concepts, though I'm not sure.
Edit: Boost would be acceptable to use, and SFINAE in particular. I'm trying to find a solution that would be familiar to most programmers, and as convenient, and canonical as possible. I could rename fmap to compose, but then the programmer would have to know to pass compose to a template function accepting fmap. That would be unfortunate, because fmap is semantically unique.
Edit 2: A trivial example of how this is used.
template <typename T>
auto double_everything(T in){
auto doublef = [](auto x){return 2*x;};
return fmap(doublef, in);
}
It generalizes maps over containers to maps over "container like" things. So double_everything(vector<int> {1, 2, 3}) returns a vector with its elements doubled. But double_everything([](int x){ return x + 1; }) returns a function whose outputs are twice the outputs of the increment function. Which is like doubling a kind of list. The abstraction has some nice properties, I'm not just making it up. At any rate, renaming the function fmap to compose doesn't answer the question.
Edit 3:
fmap for a template C takes functions from A to B to functions from C<A> to C<B> and satisfies fmap( compose(f, g) , c ) = fmap( f, fmap( g, c )). This is a nice structure preserving property.
Functions which do this for ranges already exist by different names. But ranges aren't the only templates on types. Here is fmap for std::optional:
template<typename T, typename Func>
auto fmap(Func f, optional<T> o) -> optional<f(*o)>{
if(o)
return f(*o);
else
{};
}
This implementation doesn't involve any range concepts at all, like thefmap for functions presented earlier. But it satisfies the semantic requirements for fmap.
I'm trying to define fmap for different overloads in the same way I would define a new operator * for a custom matrix type. So I would happily define fmap in terms of boost::transform_iterator. Then these algorithms would work with a function generic in terms of fmap.
Here is an example of such a function:
template <
template<typename, typename> class Cont,
typename Fmappable,
typename Alloc,
typename Func>
auto map_one_deep(Func f, Cont<Fmappable, Alloc> c){
auto g = [f](Fmappable x){ return fmap(f, x); };
return fmap(g, c);
}
now if we write
auto lists = vector<vector<int> > { {1, 2, 3}, {4, 5, 6} };
auto lists_squared = map_one_deep( [](int x){return x*x;} , lists);
lists_squared printed gives
1 4 9
16 25 36
If we instead had a vector of optionals, the optionals would be squared provided they contained elements.
I'm trying to understand how one should work with higher order functions in c++.
You can fake it with SFINAE, but you shouldn't. It's a matter of style and idiom.
Haskell is all about type classes, with a programmer expecting to have to spangle each type with all the clubs it belongs to. C++, in contrast, wants to be more implicit in specifying a type's capabilities. You've shown "vector" and "arbitrary callable" there, but why just vector? Why not an arbitrary container type? And this arbitrary container type I just wrote has an operator(), because reasons. So which one should it choose?
Bottom line, while you can use SFINAE tricks to resolve technical ambiguities, you shouldn't use them to resolve essential ambiguities. Just use two different names.
Here's the simplest compromise I found
template <typename FuncT, typename O, typename T>
auto fmap(FuncT f, function<O(T)> in){
return [f, in](T x){
return f(in(x));
};
}
Unfortunately this requires that function<Output(Input)> decorate the call site, and it litters indirections. I'm pretty sure this is the best one can do if constraining fmap is required.
Edit: You can do better. The link gives a way to restrict to callables, that also in-lines.
The function could be written like this:
template <typename FuncT, typename T>
auto fmap(FuncT f, tagged_lambda<T> in){
return tag_lambda([f, in](T x){
return f(in(x));
});
}
You could choose the version you want at the call site, by calling
fmap(g, tag_lambda({}(int x){return x + 1;}) );
or
fmap(g, function<int(int)>({}(int x){return x + 1;}) );
Given how templates work, I'm pretty sure tagging the function is required.
Here is a blog post which also talks about the issue, and discusses other options.
http://yapb-soc.blogspot.com/2012/10/fmap-in-c.html.