Class template argument deduction and default template parameters - c++

The following stripped down code doesn't work with the latest clang++5 but is accepted by g++7:
template<typename Wrapped, typename U>
struct wrapper;
template<typename Wrapped, typename U=int>
struct wrapper
{
wrapper() = default;
// Automatic deduction guide
constexpr explicit wrapper(Wrapped) noexcept {}
};
int main()
{
struct {} dummy;
constexpr auto wrapped = wrapper(dummy);
}
It fails with the following error messages:
<source>:18:30: error: no viable constructor or deduction guide for deduction of template arguments of 'wrapper'
constexpr auto wrapped = wrapper(dummy);
^
<source>:12:24: note: candidate template ignored: couldn't infer template argument 'U'
constexpr explicit wrapper(Wrapped) noexcept {}
^
<source>:4:8: note: candidate template ignored: could not match 'wrapper<Wrapped, U>' against '(anonymous struct at <source>:17:5)'
struct wrapper;
^
<source>:9:5: note: candidate function template not viable: requires 0 arguments, but 1 was provided
wrapper() = default;
^
However if I move the default template parameter =int from the class template definition to the forward declaration, everything works perfectly (U being deduced to int as expected), as if only the default template parameter in the forward declaration was taken into account when create the set of fictional function templates used by deduction guides.
I tried to read the standard wording but couldn't get much out of it for this specific case. Is only taking the default template parameter in the forward declaration the intended behaviour when generating the fictional function templates, or is this a compiler bug?

This is not a quote of the Standard per se1, but I feel confident enough to consider it an answer.
According to cppreference, on Default template arguments:
Default template arguments that appear in the declarations and the definition are merged similarly to default function arguments:
template<typename T1, typename T2 = int> class A;
template<typename T1 = int, typename T2> class A;
// the above is the same as the following:
template<typename T1 = int, typename T2 = int> class A;
But the same parameter cannot be given default arguments twice in the same scope
template<typename T = int> class X;
template<typename T = int> class X {}; // error
This implies an implicit rule: A template type argument can be given a default type in the template declaration or template definition interchangeably.
The behavior clang++5 exhibits is definitly a bug.
1) Provided by user Oliv:
[temp.param]/10
The set of default template-arguments available for use is obtained by merging the default arguments from all prior declarations of the template in the same way default function arguments are ([dcl.fct.default]). [ Example:
template<class T1, class T2 = int> class A;
template<class T1 = int, class T2> class A;
is equivalent to
template<class T1 = int, class T2 = int> class A;
 — end example ]

Related

What this template syntax "typename = T" mean?

Sometimes I see syntax like this.
template<typename T,typename = int>
int foo(){
//...
}
what part typename = int mean?
Where it can be used?
foo has two template arguments. The first is called T and the second is unnamed and defaults to int.
In your piece of code alone there is no reason to use the second argument. Unnamed template arguments often come up with SFINAE. An example from cppreference:
// primary template handles non-referenceable types:
template<class T, class = void>
struct reference_traits {
using add_lref = T;
using add_rref = T;
};
// specialization recognizes referenceable types:
template<class T>
struct reference_traits<T, std::void_t<T&>> {
using add_lref = T&;
using add_rref = T&&;
};
template<class T>
using add_lvalue_reference_t = typename reference_traits<T>::add_lref;
template<class T>
using add_rvalue_reference_t = typename reference_traits<T>::add_rref;
The only reason for the primary template to have a second argument is that it can be specialized. When possible the more specialized specialization is instantiatied. If this fails (because T& is not valid) then "substitution failure is not an error" (SFINAE) kicks in and the primary template is instantiated instead.
A simpler example of unnamed argument is when you want a template argument merely as a tag to distinguish different instantiations:
template<typename = int>
struct bar {
// ...
};
Even if the implementation of bar does not depend on the template argument you might want to have bar<double> and bar<std::string> be two distinct types.
this is rarely used ...
but this is the default value for the typename but you don't need it here because the compiler itself can overload the function automatically and get the right type for the right arguments you passed !
also it type for what typename ? it's not makes sense here !
it used when you are using nested template ...
I found out in the original reference for C++ :
The template parameter lists of template template parameters can have
their own default arguments, which are only in effect where the
template template parameter itself is in scope:
// class template, with a type template parameter with a default
template<typename T = float> struct B {};
// template template parameter T has a parameter list, which
// consists of one type template parameter with a default
template<template<typename = float> typename T> struct A
{
void f();
void g();
};
// out-of-body member function template definitions
template<template<typename TT> class T>
void A<T>::f()
{
T<> t; // error: TT has no default in scope
}
template<template<typename TT = char> class T>
void A<T>::g()
{
T<> t; // ok: t is T<char>
}
this is the link

C++20 designated initializers with templated types

How are designated initializers (C++20) supposed to work with CTAD?
This code works fine in gcc9.2, but fails with clang8
template <typename int_t=int, typename float_t=float>
struct my_pair {
int_t first;
float_t second;
};
template<typename ... ts>
my_pair(ts...) -> my_pair<ts...>;
int main() {
my_pair x{.first = 20, .second = 20.f};
static_assert( std::is_same_v<decltype(x.first), int> );
static_assert( std::is_same_v<decltype(x.second), float> );
}
Is this supposed to be valid?
See an example on
https://godbolt.org/z/KtNI43
Yes, this is supposed to be valid.
The way CTAD works is we perform overload resolution over a synthesized set of constructors to figure out what the class template parameters were. From C++17, that synthesized set of constructors is just based on the primary template's constructors and deduction guides (I'm changing the template parameter names because I find them very confusing):
template <class T=int, class U=float>
struct my_pair {
T first;
U second;
};
// default constructor
template <class T=int, class U=float>
auto __f() -> my_pair<T, U>;
// copy candidate
template <class T=int, class U=float>
auto __f(my_pair<T, U>) -> my_pair<T, U>;
// deduction guide
template <class... T>
auto __f(T...) -> my_pair<T...>;
C++20 adds a new aggregate deduction candidate. For each element of either the initializer-list or designated-initializer-list, we pick the corresponding element of the aggregate and use its type as the new candidate. For
my_pair x{.first = 20, .second = 20.f};
The type of first is T and the type of second is U, hence:
// aggregate deduction candidate
template <class T=int, class U=float>
auto __f(T, U) -> my_pair<T, U>;
Now, I wrote these four candidates as functions (because I find it easier to think of them as functions) but the wording defines them as constructors of a hypothetical class type. So when we perform overload resolution using {.first = 20, .second = 20.f}, if you squint it kind of works.
The last candidate is the best candidate (only the aggregate deduction candidate and the deduction guide are viable, the aggregate deduction candidate is more specialized), so we end up with my_pair<int, float>.
Having finished CTAD, we now start over and effectively do
my_pair<int, float> x{.first = 20, .second = 20.f};
Which of course works.

is `foo<Type1, Types...>` legal when foo is a struct which accepts single template parameter?

When playing with libstdcxx's test_property:
template<template<typename...> class Property,
typename Type1, typename... Types>
constexpr bool
test_property(typename Property<Type1, Types...>::value_type value)
{
return (Property<Type1, Types...>::value == value
&& Property<Type1, Types...>::type::value == value);
}
class Property accepts at least 1 template parameter(Type1).
Here is a use case:
static_assert(test_property<is_copy_assignable, ExceptMoveAssignClass>(false), "");
But I found clang doesn't work fine with this function:
prog.cc:29:3: error: no matching function for call to 'test_property'
test_property<std::is_copy_assignable, DeletedMoveAssignClass>(false);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
prog.cc:12:1: note: candidate template ignored: substitution failure [with Property = std::is_copy_assignable, Type1 = DeletedMoveAssignClass]: too many template arguments for class template 'is_copy_assignable'
test_property(typename Property<Type1, Types...>::value_type value)
^ ~~~~~~~~
1 error generated.
The root cause is clang doesn't allow class Property to be class that only accepts one template parameter like template< class T > struct is_copy_assignable;. Once class Property is modified into Property<Type1>, it will compile successfully:
template<template<typename...> class Property, typename Type1>
constexpr bool
ya_test_property(typename Property<Type1>::value_type value)
{
return (Property<Type1>::value == value
&& Property<Type1>::type::value == value);
}
here is demo https://wandbox.org/permlink/LlL1o57Yted5WZo5
Of course, this function is from libstdcxx, so gcc can pass compile. Is this clang's bug?
Looks like a Clang bug if I'm interpreting [temp.variadic]/7 correctly:
When N is zero, the instantiation of the expansion produces an empty
list. Such an instantiation does not alter the syntactic
interpretation of the enclosing construct, even in cases where
omitting the list entirely would otherwise be ill-formed or would
result in an ambiguity in the grammar. [ Example:
template<class... T> struct X : T... { };
template<class... T> void f(T... values) {
X<T...> x(values...);
}
template void f<>(); // OK: X<> has no base classes
// x is a variable of type X<> that is value-initialized
 — end example ]
Similarly, while std::is_copy_assignable<ExceptMoveAssignClass , > is ill-formed, an empty pack should not put us in this state. It should be equivalent to std::is_copy_assignable<ExceptMoveAssignClass>, which is well-formed.
Of course, if the pack wasn't empty, then we'd be passing too many arguments, which is ill-formed. But that is not the case.

Can constructor template cause ambiguity in the c++17 parameter deduction of class template

Consider a simple example:
template <class T>
struct foo {
template <template <class> class TT>
foo(TT<T>&&) {}
foo(foo<T>&&){}
foo() {}
};
int main() {
foo f1(foo<int>{}); //case 1.
foo<int> f2(foo<int>{}); //case 2.
}
Case 1. causes ambiguity in the template argument deduction of foo class in clang but not in gcc. I thought that template functions (here - constructor) have lower priority in overload resolution. Is it not the case here?
Error message:
prog.cc:10:14: error: ambiguous deduction for template arguments of 'foo'
foo f1(foo<int>{}); //case 1.
^
prog.cc:4:5: note: candidate function [with T = int, TT = foo]
foo(TT<T>&&) {}
^
prog.cc:5:5: note: candidate function [with T = int]
foo(foo<T>&&){}
^
1 error generated.
[clang demo] [gcc demo]
This is a Clang bug. The fact the candidate set is formed from c'tors should be immaterial, since after the candidate set is formed the best overload is chosen using the same rules for ordering implicit conversion sequences and template function ordering.
To quote [over.match.funcs]/1:
The subclauses of [over.match.funcs] describe the set of candidate
functions and the argument list submitted to overload resolution in
each of the seven contexts in which overload resolution is used. The
source transformations and constructions defined in these subclauses
are only for the purpose of describing the overload resolution
process. An implementation is not required to use such transformations
and constructions.
This clearly states that the overload resolution process is the same always. The only difference is how the candidate set is formed.
And as specified by [over.match.class.deduct]/1
A set of functions and function templates is formed comprising:
For each constructor of the primary class template designated by the template-name, if the template is defined, a function template with
the following properties:
The template parameters are the template parameters of the class template followed by the template parameters (including default
template arguments) of the constructor, if any.
The types of the function parameters are those of the constructor.
The return type is the class template specialization designated by the template-name and template arguments corresponding to the template
parameters obtained from the class template.
Each c'tor will introduce a pseudo function into the candidate set. Like this:
template <class T> foo(foo<T>&&) -> foo<T>
template <class T, template<class> class TT> foo(TT<T>&&) -> foo<T>
To illustrate further, if this was a free function bar:
template <template <class> class TT, class T>
void bar(TT<T>&&) {}
template <class T>
void bar(foo<T>&&){}
Then template function ordering would place the first overload lower than the second.
They do not have lower priority. You can solve the problem using SFINEA. This is described in Effective Modern C++ from Scott Meyers.
template <class T>
struct foo {
template <template <class> class TT, class = std::enable_if_t<!std::is_same_v<foo<T>, std::decay_t<TT<T>>>>>
foo(TT<T>&&) {}
foo(foo<T>&&){}
foo() {}
};

Unnamed class/typename in template arguments

I was looking through the documentation of SFINAE and there was this template declaration:
template<typename SomeType>
struct inner_type { typedef typename SomeType::type type; };
template <
class T,
class = typename T::type, // SFINAE failure if T has no member type
class U = typename inner_type<T>::type // hard error if T has no member type
// (guaranteed to not occur as of C++14)
> void foo(int) {}
Specifically, I'm asking about class = typename T::type. What's the point of declaring an unnamed class?
Because of the comment I thought that this will result in a compiler error when T doesn't have a member type, but that isn't the case, as foo<int, int, int>(0); compiles fine.
On the other hand
template<class T, typename = std::enable_if_t<std::is_unsigned<T>::value>>
void foo(T t) {}
doesn't compile if T is signed, and compiles if T is unsigned.
What am I missing here?
foo<int, int, int>(0); compiles fine.
Because you specify the 2nd template argument, then the default template argument (i.e. typename T::type) won't be used, then won't trigger compile error.
If you just write foo<int>(0); to make the default template argument to be used, compile will fail.
LIVE
And it's same for your 2nd sample too.
What's the point of declaring an unnamed class?
Because the template parameter won't be used for the template implementation.