Template deduction for default template argument - c++

I have a class that looks similar to this:
template <class T = char>
struct C {
T value;
};
Up to C++14, when I wanted to use it with the default template argument I always had to specify empty angle brackets:
void f() {
C<> c;
c.value = 'x';
}
Since C++17 supports class template argument deduction and explicit deduction guides, I wonder if there is a way now to make the above code work without specifying the empty angle brackets:
void f() {
C c;
c.value = 'x';
}
This code worked in GCC 8.0 if I compile it with -std=gnu++17. However, it still showed an error in Clang 6.0 and Visual Studio 15.7. Which compiler is correct in this situation?
I also tried specifying a deduction guide like this:
C() -> C<char>;
This also didn't help. Is that the correct syntax or is there even a way to specify deduction guides for a default constructor?

This program is correct:
template <class T = char>
struct C {
T value;
};
int main() {
C c;
c.value = 'x';
}
clang just doesn't fully support class template argument deduction yet (note that it does compile on trunk).
Class template argument deduction will try to perform overload resolution on the candidate set:
template <class T=char> auto __f() -> C<T>
template <class T=char> auto __f(C<T>) -> C<T>
with no initializer. That first one is a viable candidate (deducing T as char), the second one is not, so the first one is trivially the best viable candidate. So we end up with c having type C<char>. When we then do overload resolution on default construction, that works, so the program is fine.

Related

Use template argument list AND constructor arguments for determining object's template parameters [duplicate]

My understanding about the Template argument deduction for class templates proposal was to homogenize the behaviour of template functions and template classes in deduction contexts. But I think that I have misunderstood something.
If we have this template object:
template <std::size_t S, typename T>
struct test
{
static constexpr auto size = S;
using type_t = T;
test(type_t (&input)[size]) : data(input) {}
type_t (&data)[size]{};
};
I tend to use a helper function as syntactic sugar for creating test objects:
template <std::size_t S, typename T>
test<S, T> helper(T (&input)[S]) { return input; }
Which can be used as shown below:
int main()
{
int buffer[5];
auto a = helper<5, int>(buffer); // No deduction
auto b = helper<5>(buffer); // Type deduced
auto c = helper(buffer); // Type and size deduced
std::cout << a.size << b.size << c.size;
return 0;
}
The code above outputs 555 as expected. I've tried the same in Wandbox using the newer compiler setup1:
int main()
{
int buffer[5];
test<5, int> a(buffer); // No deduction: Ok.
test<5> b(buffer); // Type deduced: FAILS.
test c(buffer); // Type and size deduced: Ok.
std::cout << a.size << b.size << c.size;
return 0;
}
It looks like template argument deduction for class templates works only deducing all the parameters, I was expecting both behaviours (helper function and class template) to be the same, did I misunderstood something?
1The last compilers availables in Wandbox are gcc HEAD 7.0.1 201701 and clang HEAD 5.0.0 (trunk).
From this excellent trip report by Botond Ballo:
The feature as originally proposed included a provision for partial deduction, where you explicitly specify some of the template arguments, and leave the rest to be deduced, but this was pulled over concerns that it can be very confusing in some cases:
// Would have deduced tuple<int, string, float>,
// but tuple<int> is a well-formed type in and of itself!
tuple<int> t(42, "waldo", 2.0f);
There appears to be a contradiction here. Looking at P0091R3, it seems clear that partially specifying parameters is supposed to be allowed:
We propose to allow a template name referring to a class template as a simple-type-specifier or with partially supplied explicit template arguments in two contexts:
But the actual standards wording in the same proposal does not provide a way to handle "partially supplied explicit template arguments". template-name as a simple-type-specifier is not allowed to have template arguments.
So following the specification itself, the compiler's behavior appears to be correct.

Is it possible to make CTAD accept template type alias? [duplicate]

consider the code pasted below. I have defined a very simple class, for which the compiler generates an implicit deduction guide so it can be constructed without explicit template arguments. However, the template argument deduction does not work for constructing an object from a simple alias template which only forwards directly to the target class:
template< typename A, typename B >
struct Foo {
Foo( A const &a, B const &b )
: a_(a), b_(b)
{ }
A a_;
B b_;
};
template< typename A, typename B >
using Bar = Foo<A, B>;
auto foobar() {
Foo r{1, 2};
Bar s{3, 4};
// ../src/geo/vector_test_unit.cpp: In function 'auto foobar()':
// ../src/geo/vector_test_unit.cpp:16:6: error: missing template arguments before 's'
// Bar s{3, 4};
// ^
return 1;
}
As you can see from a code comment above, g++ is giving me an error about using the aliased template without template arguments. I was hoping in such an instance that template argument deduction would be forwarded.
So, my question: Is this by express design of the current wording of the proposal for class template argument deduction? Or is this an unfinished feature or bug in the current g++ implementation of the feature? And this would be more of a question for the authors of the proposal, or for the C++ ISO Committee, but if any of them see this: Would it be desired that the final wording of the feature include enabling alias templates such as this to also have implicit guides generated for them?
I can understand that since alias templates can have any kind of template parameters it may not always be possible for the compiler to successfully deduce the target class template arguments, but in a case like this I would expect that the compiler would be able to in the same way that it can directly for the target class.
I am building with gcc built from head only a few days ago, using --std=c++1z. The complete version info is: gcc version 7.0.0 20161201 (experimental) (Homebrew gcc HEAD- --with-jit)
This was a feature that we considered when formulating the proposal, but it was eventually cut from the C++17 feature set because we didn't yet have a good enough design for it. In particular, there are some subtleties regarding how you select and transform deduction guides from the aliased template into deduction guides for the alias template. There are also open questions as to how to behave if the alias template is not a simple alias for another template. Some examples:
template<typename T> struct Q { Q(T); }; // #1
template<typename T> struct Q<T*> { Q(T); }; // #2
template<typename U> using QP = Q<U*>;
int *x;
Q p = x; // deduces Q<int*> using #1, ill-formed
QP q = x; // deduces Q<int*> using #1, or
// deduces Q<int**> using #2?
template<typename T> Q(T) -> Q<T>; // #3
QP r = x; // can we use deduction guide #3 here?
template<typename T> Q(T*) -> Q<T**>; // #4
int **y;
QP s = y; // can we use deduction guide #4 here?
template<typename T> struct A { typedef T type; struct Y {}; };
template<typename T> using X = typename A<T>::type;
template<typename T> using Y = typename A<T>::Y;
X x = 4; // can this deduce T == int?
Y y = A<int>::Y(); // can this deduce T == int?
There are decent answers to the above questions, but tackling them adds complexity, and it seemed preferable to disallow deduction for alias templates for C++17 rather than rush something flawed in.
Update [C++20]: This topic was revisited for C++20, and we approved P1814R0, which permits class template argument deduction for alias templates.
The original example is now valid. For the examples above:
CTAD still only considers constructors from the primary template. So in QP q = x;, #2 is not considered, and instead #1's constructor is used. That constructor is implicitly converted into a guide for Q:
template<typename T> Q(T) -> Q<T>;
which is then converted into a guide for the alias template QP by deducing the right-hand side of the guide for Q (Q<T>) from the right-hand side of the alias template (Q<U*>), which deduces T = U*, then substituting that back into the guide, thereby producing the equivalent of:
template<typename U> Q(U*) -> Q<U*>;
// ... which is effectively ...
template<typename U> QP(U*) -> QP<U>;
// ... except that explicit deduction guides are not
// permitted for alias templates
That guide is then used to deduce the type of q, which deduces U = int, so the type of q is Q<int*>, so the initialization of q is ill-formed.
The initialization of r does consider deduction guide #3, which is transformed into a guide for QP as described above
The initialization of s does consider deduction guide #4; deducing Q<T**> from Q<U*> deduces nothing, so we retain the deduction guide
template<typename T> Q(T*) -> Q<T**>;
as-is, but add a constraint that the result of deduction must match the form of QP. We then deduce T = int, substitute that in to compute a result type of Q<int**>, and check that we can deduce QP<U> from Q<int**>, which we can. So the type of s is deduced as Q<int**>.
CTAD for alias templates is only supported where the right-hand side of the alias template is a simple-template-id (of the form maybe::stuff::templatename<args>). So neither X nor Y is deducible.
From http://en.cppreference.com/w/cpp/language/template_argument_deduction, "Alias templates are never deduced." So, it is by design.

C++17 typealias removes the ability of class template argument deduction [duplicate]

consider the code pasted below. I have defined a very simple class, for which the compiler generates an implicit deduction guide so it can be constructed without explicit template arguments. However, the template argument deduction does not work for constructing an object from a simple alias template which only forwards directly to the target class:
template< typename A, typename B >
struct Foo {
Foo( A const &a, B const &b )
: a_(a), b_(b)
{ }
A a_;
B b_;
};
template< typename A, typename B >
using Bar = Foo<A, B>;
auto foobar() {
Foo r{1, 2};
Bar s{3, 4};
// ../src/geo/vector_test_unit.cpp: In function 'auto foobar()':
// ../src/geo/vector_test_unit.cpp:16:6: error: missing template arguments before 's'
// Bar s{3, 4};
// ^
return 1;
}
As you can see from a code comment above, g++ is giving me an error about using the aliased template without template arguments. I was hoping in such an instance that template argument deduction would be forwarded.
So, my question: Is this by express design of the current wording of the proposal for class template argument deduction? Or is this an unfinished feature or bug in the current g++ implementation of the feature? And this would be more of a question for the authors of the proposal, or for the C++ ISO Committee, but if any of them see this: Would it be desired that the final wording of the feature include enabling alias templates such as this to also have implicit guides generated for them?
I can understand that since alias templates can have any kind of template parameters it may not always be possible for the compiler to successfully deduce the target class template arguments, but in a case like this I would expect that the compiler would be able to in the same way that it can directly for the target class.
I am building with gcc built from head only a few days ago, using --std=c++1z. The complete version info is: gcc version 7.0.0 20161201 (experimental) (Homebrew gcc HEAD- --with-jit)
This was a feature that we considered when formulating the proposal, but it was eventually cut from the C++17 feature set because we didn't yet have a good enough design for it. In particular, there are some subtleties regarding how you select and transform deduction guides from the aliased template into deduction guides for the alias template. There are also open questions as to how to behave if the alias template is not a simple alias for another template. Some examples:
template<typename T> struct Q { Q(T); }; // #1
template<typename T> struct Q<T*> { Q(T); }; // #2
template<typename U> using QP = Q<U*>;
int *x;
Q p = x; // deduces Q<int*> using #1, ill-formed
QP q = x; // deduces Q<int*> using #1, or
// deduces Q<int**> using #2?
template<typename T> Q(T) -> Q<T>; // #3
QP r = x; // can we use deduction guide #3 here?
template<typename T> Q(T*) -> Q<T**>; // #4
int **y;
QP s = y; // can we use deduction guide #4 here?
template<typename T> struct A { typedef T type; struct Y {}; };
template<typename T> using X = typename A<T>::type;
template<typename T> using Y = typename A<T>::Y;
X x = 4; // can this deduce T == int?
Y y = A<int>::Y(); // can this deduce T == int?
There are decent answers to the above questions, but tackling them adds complexity, and it seemed preferable to disallow deduction for alias templates for C++17 rather than rush something flawed in.
Update [C++20]: This topic was revisited for C++20, and we approved P1814R0, which permits class template argument deduction for alias templates.
The original example is now valid. For the examples above:
CTAD still only considers constructors from the primary template. So in QP q = x;, #2 is not considered, and instead #1's constructor is used. That constructor is implicitly converted into a guide for Q:
template<typename T> Q(T) -> Q<T>;
which is then converted into a guide for the alias template QP by deducing the right-hand side of the guide for Q (Q<T>) from the right-hand side of the alias template (Q<U*>), which deduces T = U*, then substituting that back into the guide, thereby producing the equivalent of:
template<typename U> Q(U*) -> Q<U*>;
// ... which is effectively ...
template<typename U> QP(U*) -> QP<U>;
// ... except that explicit deduction guides are not
// permitted for alias templates
That guide is then used to deduce the type of q, which deduces U = int, so the type of q is Q<int*>, so the initialization of q is ill-formed.
The initialization of r does consider deduction guide #3, which is transformed into a guide for QP as described above
The initialization of s does consider deduction guide #4; deducing Q<T**> from Q<U*> deduces nothing, so we retain the deduction guide
template<typename T> Q(T*) -> Q<T**>;
as-is, but add a constraint that the result of deduction must match the form of QP. We then deduce T = int, substitute that in to compute a result type of Q<int**>, and check that we can deduce QP<U> from Q<int**>, which we can. So the type of s is deduced as Q<int**>.
CTAD for alias templates is only supported where the right-hand side of the alias template is a simple-template-id (of the form maybe::stuff::templatename<args>). So neither X nor Y is deducible.
From http://en.cppreference.com/w/cpp/language/template_argument_deduction, "Alias templates are never deduced." So, it is by design.

How can a compiler deduce this class template with forwarding reference?

I am looking into class template deduction available since C++17. Here are the code I would like to ask about:
#include <iostream>
#include <cmath>
using std::endl;
using std::cout;
template<typename T>
struct MyAbs {
template<typename U>
MyAbs(U&& u) : t(std::forward<T>(u))
{ cout << "template" << endl;}
#ifdef ON
MyAbs(const T& t) : t(t) {}
#endif
T operator()() const
{
return std::abs(t);
}
T t;
};
/*
// may need the following
template<typename U>
MyAbs(U&&) -> MyAbs<typename std::remove_reference<U>::type>;
*/
int main()
{
const double d = 3.14;
cout << MyAbs(4.7)() << endl;
cout << MyAbs(d)() << endl;
return 0;
}
When MyAbs(const T&) is not conditionally-compiled (i.e. no -DON), both clang++ and g++ fail to deduce the template parameter, T. Given -DON=1, both compilers build the simple example above.
Firstly, I also guessed that the deduction should fail; the compiler could deduce U but not T. The compile errors I got were what I expected. Please, let me know if I am mistaken.
If I was correct on it, then, I cannot understand why the deduction with U&& succeeds when MyAbs(const T&) is added. What I expected was deducing with U&& fails, and SFINAE allows me to invoke MyAbs(const T&) instead for both cases: 4.7 and d. However, what happened is different. This program seems to invoke the template version for 4.7 and non-template version for d.
$ g++ -Wall -std=c++17 ~/a.cc -DON=1
$ ./a.out
template
4.7
3.14
It seems that the template version has suddenly become viable. Is this expected? If so, what's the reason?
Class template argument deduction happens in two stages.
Deduce the class template arguments.
Then, do the actual construction with the concrete class type.
Step 1 only helps you figure out the class template arguments. It does not do anything with regards to the actual constructor (or class template specialization) that may be used in step 2.
The existence of the constructors might drive the deduction, but if a given constructor is used to deduce that says nothing about whether or not it's used to construct.
So, when you just have:
template<typename T>
struct MyAbs {
template<typename U>
MyAbs(U&& u);
};
Class template argument deduction fails - you have no deduction guide in your constructor, T is a non-deduced context. The compiler just can't figure out what T you want in MyAbs(4.7) or MyAbs(d).
When you added this one:
template<typename T>
struct MyAbs {
template<typename U>
MyAbs(U&& u);
MyAbs(T const&);
};
Now it can! In both cases, it deduces T as double. And once it does that, then we go ahead and perform overload resolution as if we had typed MyAbs<double> to begin with.
And here, MyAbs<double>(4.7) happens to prefer the forwarding reference constructor (less cv-qualified reference) while MyAbs<double>(d) happens to prefer the other one (non-template preferred to template). And that's okay and expected, just because we used one constructor for deduction doesn't mean we have to use specifically that constructor for construction.

C++17 class template partial deduction

My understanding about the Template argument deduction for class templates proposal was to homogenize the behaviour of template functions and template classes in deduction contexts. But I think that I have misunderstood something.
If we have this template object:
template <std::size_t S, typename T>
struct test
{
static constexpr auto size = S;
using type_t = T;
test(type_t (&input)[size]) : data(input) {}
type_t (&data)[size]{};
};
I tend to use a helper function as syntactic sugar for creating test objects:
template <std::size_t S, typename T>
test<S, T> helper(T (&input)[S]) { return input; }
Which can be used as shown below:
int main()
{
int buffer[5];
auto a = helper<5, int>(buffer); // No deduction
auto b = helper<5>(buffer); // Type deduced
auto c = helper(buffer); // Type and size deduced
std::cout << a.size << b.size << c.size;
return 0;
}
The code above outputs 555 as expected. I've tried the same in Wandbox using the newer compiler setup1:
int main()
{
int buffer[5];
test<5, int> a(buffer); // No deduction: Ok.
test<5> b(buffer); // Type deduced: FAILS.
test c(buffer); // Type and size deduced: Ok.
std::cout << a.size << b.size << c.size;
return 0;
}
It looks like template argument deduction for class templates works only deducing all the parameters, I was expecting both behaviours (helper function and class template) to be the same, did I misunderstood something?
1The last compilers availables in Wandbox are gcc HEAD 7.0.1 201701 and clang HEAD 5.0.0 (trunk).
From this excellent trip report by Botond Ballo:
The feature as originally proposed included a provision for partial deduction, where you explicitly specify some of the template arguments, and leave the rest to be deduced, but this was pulled over concerns that it can be very confusing in some cases:
// Would have deduced tuple<int, string, float>,
// but tuple<int> is a well-formed type in and of itself!
tuple<int> t(42, "waldo", 2.0f);
There appears to be a contradiction here. Looking at P0091R3, it seems clear that partially specifying parameters is supposed to be allowed:
We propose to allow a template name referring to a class template as a simple-type-specifier or with partially supplied explicit template arguments in two contexts:
But the actual standards wording in the same proposal does not provide a way to handle "partially supplied explicit template arguments". template-name as a simple-type-specifier is not allowed to have template arguments.
So following the specification itself, the compiler's behavior appears to be correct.