C++ Concepts Same and Assignable - c++

I have been experimenting with C++ concepts recently. I am trying the definitions from the following Ranges Extensions document:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4569.pdf
The definitions and usages of Same are confusing me. For reasons unknown to me, the authors did not give an explicit definition. So I am using:
template <class T, class U>
concept bool Same()
{
return std::is_same<T, U>::value;
}
The problem is that the document gives the following definition for Assignable:
template <class T, class U>
concept bool Assignable()
{
return Common<T, U>() && requires(T&& a, U&& b) {
{ std::forward<T>(a) = std::forward<U>(b) } -> Same<T&>;
};
}
It does not work (under GCC 6.3): a simple Assignable<int&, int&&>() concept check gives me false (I have verified that the Common part is OK). I have to change Same<T&> to T& to make it seemingly work. The same Same<Type> check is used in some other places too.
My questions are:
Is my definition of Same correct?
Why is Same<T&> used instead of T&? What are the differences?
Thanks for any help.

After attacking the problem during the weekend, I think I have found the answer myself.
Eric Niebler and Casey Carter have a more refined definition of Same that supports multiple template arguments (not just two), but my definition should be basically right for the two-argument case.
When using -> Type, the purpose is that the expression in the brackets can be implicitly converted to Type. When using -> Same<Type>, the purpose is that the expression in the brackets is exactly Type. So they are different.
However, there is a gotcha. The constraint check is quite complicated, and even experts like Eric and Casey made a mistake and gave wrong definitions in N4569. Eric discussed the issue on GitHub:
https://github.com/ericniebler/stl2/issues/330
When used the way it was given in N4569, it means the expression should be able to be passed to an imagined function template like
template <typename U>
f(U)
requires Same<T&, U>()
This doesn't work—if the expression passed in is an lvalue of T, the deduced U is T instead of T&. The solution is use Same<T&>&& in Assignable. It will result in the following imagined function template:
template <typename U>
f(U&&)
requires Same<T&, U>()
Now everything is OK—if the expression passed in is an lvalue of T, U has to be deduced as T&.
Playing with concepts is a good practice for me, but I probably should find their code earlier. They have a complete set of concepts in the following GitHub repository:
https://github.com/CaseyCarter/cmcstl2
People interested in C++ concepts should look into it.

The issue with the definition of Same is a bit more subtle: the Ranges TS requires implementations to treat the constraint Same<T, U>() as equivalent to Same<U, T>(), i.e., recognize the symmetry of "T is the same type as U":
template <class T, class U>
requires Same<T, U>()
void foo(); // #1
template <class T, class U>
requires Same<U, T>()
void foo(); // redeclaration of #1
template <class T>
requires Same<int, T>()
void bar(); // #2
template <class T>
requires Same<T, int>()
void bar(); // redeclaration of #2
This equivalence can't be expressed in the language, since the rules for normalization of constraints recognize each of:
is_same_v<T, U>
is_same_v<U, T>
is_same_v<T, int>
is_same_v<int, T>
as distinct atomic constraints. This requires implementation of Same via a compiler intrinsic.

Related

Is template function with `auto` template type the same as with T-type in C++20? [duplicate]

All standard references below refers to the current ISO Standard Working Draft, generated on 2020-06-22.
[dcl.fct]/18 states that [extract, emphasis mine]:
An abbreviated function template is a function declaration that has one or more generic parameter type placeholders ([dcl.spec.auto]). An abbreviated function template is equivalent to a function template ([temp.fct]) whose template-parameter-list includes one invented type template-parameter for each generic parameter type placeholder of the function declaration, in order of appearance. [...]
Such that the following to function declarations are areuably equivalent:
template <typename T>
void f(T);
void f(auto); // re-declaration
We may note, however, that the example of [dcl.fct]/18 states that
[...]
These declarations are functionally equivalent (but not equivalent) to the following declarations.
[...]
which may arguably (I'm unsure how interpret this) conflict with the equivalence statement in the prior passage.
Now, both GCC 10.1.0 and Clang 10.0.0 (as well as GCC:HEAD and Clang:HEAD) have some mixed behavior here. If we declare a function template and later define it (/re-declare it) using a mixed classical function template syntax with abbreviated function template syntax, Clang accepts most cases (defining a previously declared function) whereas GCC rejects all (sees the (attempted) re-declarations as separately declared functions with subsequent ambiguity failures in overload resolution):
// A1: Clang OK, GCC error
template <typename T>
void a(T);
void a(auto) {}
// B1: Clang OK, GCC error
void b(auto);
template <typename T>
void b(T) {}
// C1: Clang OK, GCC error
template <typename T, typename U>
void c(T, U);
void c(auto, auto) {}
// D1: Clang OK, GCC error
template <typename T, typename U>
void d(T, U);
template <typename T>
void d(T, auto) {}
// E1: Clang error, GCC error
template <typename T>
void e(T, auto);
template <typename T>
void e(auto, T) {}
int main() {
a(0); // Clang OK, GCC error.
b(0); // Clang OK, GCC error.
c(0, '0'); // Clang OK, GCC error.
d(0, '0'); // Clang OK, GCC error.
e(0, '0'); // Clang error, GCC error.
}
Curiously, if we make the function template a class member function template, both GCC and Clang accepts cases A1 through D1, but both rejects the final case E1 above:
// A2: OK
struct Sa {
template <typename T>
void a(T);
};
void Sa::a(auto) {}
// B2: OK
struct Sb {
void b(auto);
};
template <typename T>
void Sb::b(T) {}
// C2: OK
struct Sc {
template <typename T, typename U>
void c(T, U);
};
void Sc::c(auto, auto) {}
// D2: OK
struct Sd {
template <typename T, typename U>
void d(T, U);
};
template <typename T>
void Sd::d(T, auto) {}
// E2: Error
struct Se {
template <typename T>
void e(T, auto);
};
template <typename T>
void Se::e(auto, T) {}
with the following error messages:
GCC
error: no declaration matches 'void Se::e(auto:7, T)'
note: candidate is:
'template<class T, class auto:6> void Se::e(T, auto:6)'
Clang
error: out-of-line definition of 'e' does not match
any declaration in 'Se'
Now, the name of a type template parameter is not required to be consistent over re-declaration (or a definition) of a function template, as just names a generic type placeholder.
GCC's error message is particularly interesting, hinting that invented type template parameters are treated as concrete types rather than generic type placeholders.
Question:
Which of GCC and Clang are correct regarding cases A1 through D1 (rejecting and accepting, respectively)? Are GCC and Clang correct to reject case E2 above? What standard passage (of the working draft) unambiguously supports them?
This:
template <typename T>
void e(T, auto);
Translates to this:
template<typename T, typename U>
void e(T, U);
By contrast, this:
template <typename T>
void e(auto, T) {}
translates to:
template <typename T, typename U>
void e(U, T) {}
Remember that abbreviated function template parameters are placed at the end of the template parameter list. So those aren't declaring the same template, due to reversing the order of the template parameters. The first one declares one template, the second one declares and defines a different template.
You don't get a compile error just from this because the second definition is also a declaration. However, when you use a class member, out-of-member definitions are not declarations. Therefore, they must have a matching in-member declaration. Which they don't; hence the errors.
As for the others, the "functionally equivalent (but not equivalent)" text is a non-normative notation. The actual normative text that you quoted clearly states that these are "equivalent", not merely "functionally equivalent". And since the term "equivalent", per [temp.over.link]/7, is used to match declarations and definitions, it seems to me that the standard states that the A through D cases are fine.
What's weird is that this non-normative text was introduced by the same proposal that introduced the normative text. However, the proposal that it inherited the ConceptName auto syntax from seems clear that it means "equivalent", not "functionally equivalent".
So in terms of normative text, everything seems clear. But the presence of the non-normative contradiction suggests either an editorial problem or an actual defect in the spec.
While the standard itself is clear and normatively reasonable in terms of wording, it appears that this is not what the writers of the standard intended.
P0717 introduced the concept of "functionally equivalent" as being distinct from "equivalent". And that proposal was accepted. However, P0717 was introduced early in the process of adopting the Concepts TS for C++20. In that proposal, it specifically spoke of terse template syntax, and EWG explicitly voted in favor of adopting "functionally equivalent" wording for it instead of the Concepts TS "equivalent" wording.
That is, P0717 makes it clear that the committee intended for users to be required to use consistent syntax.
However, terse template syntax from Concepts TS was removed from C++20 (or rather, never really added). Which meant that any "functionally equivalent" wording never made it in, since the feature never made it in.
Then P1141 happened, which added abbreviated template syntax, that covered much of the ground of the Concepts TS terse template syntax. But, despite one of the authors of P0717 being an author of P1141, apparently someone made a mistake in the wording and nobody caught it. This would explain why the non-normative text calls out the lack of true equivalence: because that was actually the committee's intent.
So it's very possible that this is a mistake in the normative text.

Equivalence between function templates and abbreviated function templates

All standard references below refers to the current ISO Standard Working Draft, generated on 2020-06-22.
[dcl.fct]/18 states that [extract, emphasis mine]:
An abbreviated function template is a function declaration that has one or more generic parameter type placeholders ([dcl.spec.auto]). An abbreviated function template is equivalent to a function template ([temp.fct]) whose template-parameter-list includes one invented type template-parameter for each generic parameter type placeholder of the function declaration, in order of appearance. [...]
Such that the following to function declarations are areuably equivalent:
template <typename T>
void f(T);
void f(auto); // re-declaration
We may note, however, that the example of [dcl.fct]/18 states that
[...]
These declarations are functionally equivalent (but not equivalent) to the following declarations.
[...]
which may arguably (I'm unsure how interpret this) conflict with the equivalence statement in the prior passage.
Now, both GCC 10.1.0 and Clang 10.0.0 (as well as GCC:HEAD and Clang:HEAD) have some mixed behavior here. If we declare a function template and later define it (/re-declare it) using a mixed classical function template syntax with abbreviated function template syntax, Clang accepts most cases (defining a previously declared function) whereas GCC rejects all (sees the (attempted) re-declarations as separately declared functions with subsequent ambiguity failures in overload resolution):
// A1: Clang OK, GCC error
template <typename T>
void a(T);
void a(auto) {}
// B1: Clang OK, GCC error
void b(auto);
template <typename T>
void b(T) {}
// C1: Clang OK, GCC error
template <typename T, typename U>
void c(T, U);
void c(auto, auto) {}
// D1: Clang OK, GCC error
template <typename T, typename U>
void d(T, U);
template <typename T>
void d(T, auto) {}
// E1: Clang error, GCC error
template <typename T>
void e(T, auto);
template <typename T>
void e(auto, T) {}
int main() {
a(0); // Clang OK, GCC error.
b(0); // Clang OK, GCC error.
c(0, '0'); // Clang OK, GCC error.
d(0, '0'); // Clang OK, GCC error.
e(0, '0'); // Clang error, GCC error.
}
Curiously, if we make the function template a class member function template, both GCC and Clang accepts cases A1 through D1, but both rejects the final case E1 above:
// A2: OK
struct Sa {
template <typename T>
void a(T);
};
void Sa::a(auto) {}
// B2: OK
struct Sb {
void b(auto);
};
template <typename T>
void Sb::b(T) {}
// C2: OK
struct Sc {
template <typename T, typename U>
void c(T, U);
};
void Sc::c(auto, auto) {}
// D2: OK
struct Sd {
template <typename T, typename U>
void d(T, U);
};
template <typename T>
void Sd::d(T, auto) {}
// E2: Error
struct Se {
template <typename T>
void e(T, auto);
};
template <typename T>
void Se::e(auto, T) {}
with the following error messages:
GCC
error: no declaration matches 'void Se::e(auto:7, T)'
note: candidate is:
'template<class T, class auto:6> void Se::e(T, auto:6)'
Clang
error: out-of-line definition of 'e' does not match
any declaration in 'Se'
Now, the name of a type template parameter is not required to be consistent over re-declaration (or a definition) of a function template, as just names a generic type placeholder.
GCC's error message is particularly interesting, hinting that invented type template parameters are treated as concrete types rather than generic type placeholders.
Question:
Which of GCC and Clang are correct regarding cases A1 through D1 (rejecting and accepting, respectively)? Are GCC and Clang correct to reject case E2 above? What standard passage (of the working draft) unambiguously supports them?
This:
template <typename T>
void e(T, auto);
Translates to this:
template<typename T, typename U>
void e(T, U);
By contrast, this:
template <typename T>
void e(auto, T) {}
translates to:
template <typename T, typename U>
void e(U, T) {}
Remember that abbreviated function template parameters are placed at the end of the template parameter list. So those aren't declaring the same template, due to reversing the order of the template parameters. The first one declares one template, the second one declares and defines a different template.
You don't get a compile error just from this because the second definition is also a declaration. However, when you use a class member, out-of-member definitions are not declarations. Therefore, they must have a matching in-member declaration. Which they don't; hence the errors.
As for the others, the "functionally equivalent (but not equivalent)" text is a non-normative notation. The actual normative text that you quoted clearly states that these are "equivalent", not merely "functionally equivalent". And since the term "equivalent", per [temp.over.link]/7, is used to match declarations and definitions, it seems to me that the standard states that the A through D cases are fine.
What's weird is that this non-normative text was introduced by the same proposal that introduced the normative text. However, the proposal that it inherited the ConceptName auto syntax from seems clear that it means "equivalent", not "functionally equivalent".
So in terms of normative text, everything seems clear. But the presence of the non-normative contradiction suggests either an editorial problem or an actual defect in the spec.
While the standard itself is clear and normatively reasonable in terms of wording, it appears that this is not what the writers of the standard intended.
P0717 introduced the concept of "functionally equivalent" as being distinct from "equivalent". And that proposal was accepted. However, P0717 was introduced early in the process of adopting the Concepts TS for C++20. In that proposal, it specifically spoke of terse template syntax, and EWG explicitly voted in favor of adopting "functionally equivalent" wording for it instead of the Concepts TS "equivalent" wording.
That is, P0717 makes it clear that the committee intended for users to be required to use consistent syntax.
However, terse template syntax from Concepts TS was removed from C++20 (or rather, never really added). Which meant that any "functionally equivalent" wording never made it in, since the feature never made it in.
Then P1141 happened, which added abbreviated template syntax, that covered much of the ground of the Concepts TS terse template syntax. But, despite one of the authors of P0717 being an author of P1141, apparently someone made a mistake in the wording and nobody caught it. This would explain why the non-normative text calls out the lack of true equivalence: because that was actually the committee's intent.
So it's very possible that this is a mistake in the normative text.

How can I create deduction guides for template aliases in C++20?

Suppose I have a class/struct template together with an explicit deduction guide for its constructor. Let this class have two template parameters of which one can get deduced by the deduction guide, for the other it can not.
template <int Q, typename T>
struct Foo {
template <typename F>
Foo(F&&) { }
};
template <typename T>
using alias = T;
template <typename T>
struct alias2 { using type = T; };
template <int Q, typename F>
Foo(F&& f) -> Foo<Q, alias<F>>; // deduction guide, but cannot deduce Q yet
template <typename T>
using Bar = Foo<1, T>; // create alias that fixes Q
/* This should generate a deduction guide for Bar<T> by first
"copying" Foo's deduction guide, deducing from Foo<Q, alias<F>>
and Foo<1, T> that Q=1 and T=alias<F>=F, thus generating
<template F>
Bar(F&&) -> Bar<1, F>;
if this was correct syntax. */
int main() {
Bar f{ 5 };
}
If I now create an alias that will explicitly specify the formerly undeducable parameter, as far as I understand, the implicitly-generated deduction guide of this alias should be able to fully deduce both template arguments (by the rules of standard template argument deduction), even if one type is undeduced in the defining class template.
But what can I do in the scenario where I do not use alias, but alias2, i.e. change the deduction guide to
template <int Q, typename F>
Foo(F&& f) -> Foo<Q, typename alias2<F>::type>;
According to the documentation, this would now introduce a non-deduced context (as the template parameter appears left to a scope operator ::), so the template argument deduction for T=F should fail (which apparently does).
Question 1: If this theory is correct, is there something I can do about it? Suppose I do not want to use a trivial identity alias, but a more complex type transformation that will ultimatively have the shape of a typename transformation<Input>::result in the deduction guide.
Question 2: Even now, my theory fails when I remove Q entirely, as the following code will be accepted (by GCC-10/11):
template <typename T>
struct Foo {
template <typename F>
Foo(F&&) { }
};
template <typename T>
struct alias2 { using type = T; };
template <typename F>
Foo(F&& f) -> Foo<typename alias2<F>::type>;
template <typename T>
using Bar = Foo<T>;
template <typename T>
void some(typename alias2<T>::type) { }
int main() {
Bar f{ 5 };
}
Why is the compiler able to deduce T from F even if this is a non-deduced context?
To do what you want, C++ would have to be able to invert a Turing-complete subprogram.
Turing-complete programs are not only not-invertible, it isn't possible to determine if a given Turing-complete program is invertible or not. You can define sublanguages where they are all invertible, but those sublanguages lack Turing-complete power.
Deducing the Bar alias argument:
template <typename T>
using Bar = Foo<1, T>;
requires inverting the 2nd template argument alias<F> to find F. When alias is a trivial template alias, this is possible, permitted, and required by the C++ standard.
When alias2 evaluates is to a foo<F>::type, such a construct is capable of turing-complete computation. Rather than have compilers try to invert such a computation, the C++ standard uniformly says "don't try". It uses "dependent type" usually to block such an inversion attempt.
In your second case, Bar is a trivial alias of Foo. Foo has a deduction guide. That deduction guide tells how to get from F to T, so the compiler doesn't have to invert any potentially Turing-complete programs in order to determine T.
The C++ language has a bunch of wording to permit template aliases that are just renaming of parameters or the like to act as if they were the original type. Originally even a toy alias would block a bunch of this kind of deduction; but this was found to be a bad plan. So they added text to the standard to describe what kind of template aliases where "trivial" like that, and modified the wording of deduction rules so that they would be treated as transparent.
In order to invert an arbitrary Turing-complete program (in fact, almost any structurally non-trivial type transformation) during type deduction, you must explicitly give the inversion.
Once you have accepted that, it becomes battle with syntax, not a conceptual one.
I'm unaware of the current status of user-defined template deduction guides of alias templates. Last I heard it wasn't supported, but I haven't checked recently.

Ranges TS: Same concept differs from std::is_same? [duplicate]

I have been experimenting with C++ concepts recently. I am trying the definitions from the following Ranges Extensions document:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4569.pdf
The definitions and usages of Same are confusing me. For reasons unknown to me, the authors did not give an explicit definition. So I am using:
template <class T, class U>
concept bool Same()
{
return std::is_same<T, U>::value;
}
The problem is that the document gives the following definition for Assignable:
template <class T, class U>
concept bool Assignable()
{
return Common<T, U>() && requires(T&& a, U&& b) {
{ std::forward<T>(a) = std::forward<U>(b) } -> Same<T&>;
};
}
It does not work (under GCC 6.3): a simple Assignable<int&, int&&>() concept check gives me false (I have verified that the Common part is OK). I have to change Same<T&> to T& to make it seemingly work. The same Same<Type> check is used in some other places too.
My questions are:
Is my definition of Same correct?
Why is Same<T&> used instead of T&? What are the differences?
Thanks for any help.
After attacking the problem during the weekend, I think I have found the answer myself.
Eric Niebler and Casey Carter have a more refined definition of Same that supports multiple template arguments (not just two), but my definition should be basically right for the two-argument case.
When using -> Type, the purpose is that the expression in the brackets can be implicitly converted to Type. When using -> Same<Type>, the purpose is that the expression in the brackets is exactly Type. So they are different.
However, there is a gotcha. The constraint check is quite complicated, and even experts like Eric and Casey made a mistake and gave wrong definitions in N4569. Eric discussed the issue on GitHub:
https://github.com/ericniebler/stl2/issues/330
When used the way it was given in N4569, it means the expression should be able to be passed to an imagined function template like
template <typename U>
f(U)
requires Same<T&, U>()
This doesn't work—if the expression passed in is an lvalue of T, the deduced U is T instead of T&. The solution is use Same<T&>&& in Assignable. It will result in the following imagined function template:
template <typename U>
f(U&&)
requires Same<T&, U>()
Now everything is OK—if the expression passed in is an lvalue of T, U has to be deduced as T&.
Playing with concepts is a good practice for me, but I probably should find their code earlier. They have a complete set of concepts in the following GitHub repository:
https://github.com/CaseyCarter/cmcstl2
People interested in C++ concepts should look into it.
The issue with the definition of Same is a bit more subtle: the Ranges TS requires implementations to treat the constraint Same<T, U>() as equivalent to Same<U, T>(), i.e., recognize the symmetry of "T is the same type as U":
template <class T, class U>
requires Same<T, U>()
void foo(); // #1
template <class T, class U>
requires Same<U, T>()
void foo(); // redeclaration of #1
template <class T>
requires Same<int, T>()
void bar(); // #2
template <class T>
requires Same<T, int>()
void bar(); // redeclaration of #2
This equivalence can't be expressed in the language, since the rules for normalization of constraints recognize each of:
is_same_v<T, U>
is_same_v<U, T>
is_same_v<T, int>
is_same_v<int, T>
as distinct atomic constraints. This requires implementation of Same via a compiler intrinsic.

What is the template<typename T, T t> idiom?

I was reading this and was trying to understand what N3601 was about. It said this idiom comes up a lot in a web search, but I couldn't find anything. What is the
template<typename T, T t>
idiom, what does it solve, how is it used, what is are Implicit template parameters, and what does the proposal aim to fix?
The problem that is being solved is deducing types from template non-type parameters.
Given:
template<typename T> void foo(T);
template<typename T, T> void bar();
it is possible to deduce T for foo (for example, foo(10) will result in T being deduced to be int), but it is not possible to deduce T for bar (bar<10>() will simply not compile, you have to write it as bar<int,10>()).
N3601 proposes fixing this by introducing the syntax:
template<using typename T, T> void bar();
which will allow bar<10>() to compile and cause the type T to be deduced.
The paper introduction is misleading: the idiom is actually
template <typename T, T t>
It denotes a template which depends on a type T and a value t of that type. The notation is a bit heavy since in most situations the type could be deduced from the value itself.
E.g.
// the current definition notation
template <typename T, T t> void f() { t.f(); };
//// the proposed definition notation
//// the parameter t depends on an implicit typename parameter T
// template <using typename T, T t> void f() { t.f(); };
struct foo {
void f(){
// some computation
}
};
foo bar;
int main(){
// the current instantiation notation
f<foo,bar>();
//// the proposed instantiation notation
//// we know that bar is of type foo, so we don't need to specify it
// f<bar>();
}
The proposal is about introducing a bit of "syntactic sugar" to make the notation easier to write.
Also, the example given above is trivial in its description (and possibly wrong, since template parameters need to be constexpr), but the paper describes several situations where the current notation can become quite hairy, reducing readability and overall ease of programming.