Something like CTAD for implicit conversions without inheritance? [duplicate] - c++

This question already has answers here:
Why does the implicit type conversion not work in template deduction?
(2 answers)
Implicit type conversion with template
(4 answers)
Template function argument deduction with an implicit conversion
(1 answer)
Closed 10 days ago.
I'm using C++20.
template <typename T> struct Foo {};
template <typename T> struct Bar {
constexpr operator Foo<T>() const { return {}; }
};
template <typename T> void foo(Foo<T>) {}
void bar(Foo<int>) {}
int main() {
Bar<int> b;
bar(b); // fine
foo(b); // error
}
Godbolt link so you can test online:
https://godbolt.org/z/vYjs5nMxh
The error
<source>: In function 'int main()':
<source>:13:6: error: no matching function for call to 'foo(Bar<int>&)'
13 | foo(b);
| ~~~^~~
<source>:7:28: note: candidate: 'template<class T> void foo(Foo<T>)'
7 | template <typename T> void foo(Foo<T>) {}
| ^~~
<source>:7:28: note: template argument deduction/substitution failed:
<source>:13:6: note: 'Bar<int>' is not derived from 'Foo<T>'
13 | foo(b);
| ~~~^~~
Compiler returned: 1
Is there a way to get deduction to work without requiring inheritance?

Related

Deduce template parameter of class member from constructor of class

I have a class A which contains a templated member B whose exact type should be deduced from A's constructor. The way this is supposed to work is that, as shown in the below example, B can be instantiated with either 1 or 2 parameters to its constructor (deduction guide will tell) but will take a const char* in any case. When I instantiate A with the const char* argument for the constructor, an object B should be instantiated from the const char* as A only takes a B object. However, this is how far I get:
#include <iostream>
template <bool LengthOpt>
struct B
{
B(const char*) { }
B(const char*, size_t) { }
void print() {
if constexpr (LengthOpt) {
std::cout << "LengthOpt is set" << std::endl;
}
}
};
B(const char*) -> B<false>;
B(const char*, size_t) -> B<true>;
template <template <bool LengthOpt> class T>
struct A
{
A(T is) : is_{is} {
}
void print() {
is_.print();
}
T is_;
};
int main()
{
A a("hello");
a.print();
}
And it yields those errors:
<source>:24:7: error: use of template template parameter 'T' requires template arguments; argument deduction not allowed in function prototype
A(T is) : is_{is} {
^
<source>:21:43: note: template is declared here
template <template <bool LengthOpt> class T>
^
<source>:32:5: error: use of template template parameter 'T' requires template arguments; argument deduction not allowed in non-static struct member
T is_;
^
<source>:21:43: note: template is declared here
template <template <bool LengthOpt> class T>
^
<source>:37:7: error: no viable constructor or deduction guide for deduction of template arguments of 'A'
A a("hello");
^
<source>:22:8: note: candidate template ignored: could not match 'A<T>' against 'const char *'
struct A
^
<source>:22:8: note: candidate function template not viable: requires 0 arguments, but 1 was provided
My take on the problem is that the compiler doesn't know that I want to instantiate an object B in A's constructor, as the template template argument specifies nothing. It could very well be just any object that takes one template parameter.
I'm scratching my head right now on how to resolve this. Is it even possible or am I scratching a limitation in C++ again?

Why does an optional argument in a template constructor for enable_if help the compiler to deduce the template parameter? [duplicate]

This question already has answers here:
SFINAE: std::enable_if as function argument
(2 answers)
Closed 10 months ago.
The minimal example is rather short:
#include <iostream>
#include <array>
#include <type_traits>
struct Foo{
//template <class C>
//Foo(C col, typename std::enable_if<true,C>::type* = 0){
// std::cout << "optional argument constructor works" << std::endl;
//}
template <class C>
Foo(typename std::enable_if<true, C>::type col){
std::cout << "no optional argument constructor works NOT" << std::endl;
}
};
int main()
{
auto foo = Foo(std::array<bool,3>{0,0,1});
}
The first constructor works as expected. However the second constructor does not compile and I get
error: no matching function for call to ‘Foo::Foo(std::array)’
However the given explanation
note: template argument deduction/substitution failed
does not help, as std::enable_if<true, C>::type should be C and such the first argument in both constructors should look exactly the same to the compiler. I'm clearly missing something. Why is the compiler behaving differently and are there any other solution for constructors and enable_if, which do not use an optional argument?
Complete error message:
main.cpp:18:45: error: no matching function for call to ‘Foo::Foo(std::array)’
18 | auto foo = Foo(std::array<bool,3>{0,0,1});
| ^
main.cpp:11:5: note: candidate: ‘template Foo::Foo(typename std::enable_if::type)’
11 | Foo(typename std::enable_if<true, C>::type col){
| ^~~
main.cpp:11:5: note: template argument deduction/substitution failed:
main.cpp:18:45: note: couldn’t deduce template parameter ‘C’
18 | auto foo = Foo(std::array<bool,3>{0,0,1});
| ^
main.cpp:5:8: note: candidate: ‘constexpr Foo::Foo(const Foo&)’
5 | struct Foo{
| ^~~
main.cpp:5:8: note: no known conversion for argument 1 from ‘std::array’ to ‘const Foo&’
main.cpp:5:8: note: candidate: ‘constexpr Foo::Foo(Foo&&)’
main.cpp:5:8: note: no known conversion for argument 1 from ‘std::array’ to ‘Foo&&’
Template argument deduction does not work this way.
Suppose you have a template and a function using a type alias of that template:
template <typename T>
struct foo;
template <typename S>
void bar(foo<S>::type x) {}
When you call the function, eg foo(1) then the compiler will not try all instantiations of foo to see if any has a type that matches the type of 1. And it cannot do that because foo::type is not necessarily unambiguous. It could be that different instantiations have the same foo<T>::type:
template <>
struct foo<int> { using type = int; };
template <>
struct foo<double> { using type = int; };
Instead of even attempting this route and potentially resulting in ambiguity, foo<S>::type x is a nondeduced context. For details see What is a nondeduced context?.
Template argument deduction fails because C appears in a Non-deduced context. The linked page lists
The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id
as a non-deduced context.
They also mention another example further down:
For example, in A<T>::B<T2>, T is non-deduced because of rule #1 (nested name specifier), and T2 is non-deduced because it is part of the same type name, but in void(*f)(typename A<T>::B, A<T>), the T in A<T>::B is non-deduced (because of the same rule), while the T in A<T> is deduced.
The other answers already explain why argument deduction did not work here. If you want to enable_if your constructor, you can simply put the condition in the template list like this:
struct Foo{
// your condition here ---v
template <class C, typename std::enable_if_t< true >* = nullptr>
Foo(C col) {
std::cout << "constructor" << std::endl;
}
};

Why is implicit conversion not applied to templated function parameter?

I'm having an issue with some template stuff that I've narrowed down to the following example (C++17):
template <typename T> struct item {
operator item<const T> () const { return item<const T>(); }
};
void conversionToConstRefWorks (const item<const int> &) { }
template <typename T>
void butNotWhenTemplated (const item<const T> &) { }
int main () {
item<int> i;
item<const int> ci;
// these all compile fine:
conversionToConstRefWorks(ci);
conversionToConstRefWorks(i);
butNotWhenTemplated(ci);
// but this one fails:
butNotWhenTemplated(i);
}
In that example:
item<T> has an implicit conversion operator to item<const T>, and
The conversion seems to work in conversionToConstRefWorks(), but
The conversion seems to be missed in butNotWhenTemplated(), where an item<const int> can be passed just fine but passing an item<int> fails to compile.
Compilation of that example fails (GCC 9.3) with:
g++ --std=c++17 -W -Wall -pedantic -Wno-unused-variable const_interop.cpp -o const_interop
const_interop.cpp: In function ‘int main()’:
const_interop.cpp:54:24: error: no matching function for call to ‘butNotWhenTemplated(item<int>&)’
54 | butNotWhenTemplated(i);
| ^
const_interop.cpp:40:6: note: candidate: ‘template<class T> void butNotWhenTemplated(const item<const T>&)’
40 | void butNotWhenTemplated (const item<const T> &) {
| ^~~~~~~~~~~~~~~~~~~
const_interop.cpp:40:6: note: template argument deduction/substitution failed:
const_interop.cpp:54:24: note: types ‘const T’ and ‘int’ have incompatible cv-qualifiers
54 | butNotWhenTemplated(i);
| ^
The root error seems to be:
types ‘const T’ and ‘int’ have incompatible cv-qualifiers
I understand what that means in a literal sense, but I don't understand why it is happening. My expectation is that the item<int> :: operator item<const int> () const conversion operator would be applied when calling butNotWhenTemplated(i) just as it was applied when calling conversionToConstRefWorks(i), and that int would be selected for T.
My main question is: Why isn't this compiling?
My other question is: For reasons outside the scope of this post, butNotWhenTemplated has to be a template and has to specify <const T> to all item parameters, and I can't explicitly specify template parameters when calling it. Is there a way to make this work with those constraints?
Here it is on ideone (GCC 8.3).
item<int> i;
template <typename T> void butNotWhenTemplated (const item<const T> &) { }
butNotWhenTemplated(i);
According to template argument substitution rules, no T could be found for item<const T> to match item<int>. This fails with an hard error way before any conversion (builtin or user-defined) could be considered.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later. However, if deduction succeeds for all parameters that participate in template argument deduction, and all template arguments that aren't deduced are explicitly specified or defaulted, then the remaining function parameters are compared with the corresponding function arguments.
Try this overload:
template <typename T>
void butNotWhenTemplated(const item<const T>&) { }
template <typename T>
void butNotWhenTemplated(const item<T>& x) {
butNotWhenTemplated<const T>(x);
}
Addendum:
You're trying to pass by reference to const, but implicit conversion creates a copy of your object, even in the non-template case. You might want to rethink your design here.

bar(type (&)[x]) definition does not match bar(type (&)[x]) declaration? [duplicate]

This question already has an answer here:
a constant in dependent base class make out-of-line definition not matched?
(1 answer)
Closed 2 years ago.
This is a follow up of Template class type alias failing substitution in member declaration
Consider this code:
// A
template <typename T>
struct foo {
using type = unsigned;
template <type x>
void bar(type (&)[x]);
};
template <typename T>
template <typename foo<T>::type x>
void foo<T>::bar(type (&)[x]){}
gcc emits the following error:
<source>:13:6: error: no declaration matches 'void foo<T>::bar(foo<T>::type (&)[x])'
13 | void foo<T>::bar(type (&)[x]){}
| ^~~~~~
<source>:8:10: note: candidate is: 'template<class T> template<unsigned int x> void foo<T>::bar(foo<T>::type (&)[x])'
8 | void bar(type (&)[x]);
| ^~~
<source>:4:8: note: 'struct foo<T>' defined here
4 | struct foo {
| ^~~
Compiler returned: 1
clang:
<source>:13:14: error: out-of-line definition of 'bar' does not match any declaration in 'foo<T>'
void foo<T>::bar(type (&)[x]){}
^~~
1 error generated.
Compiler returned: 1
When I remove what is identical in the erroneous definition and the candidate I get this:
// B
template <typename T>
struct foo {
using type = unsigned;
template <type x>
void bar();
};
template <typename T>
template <typename foo<T>::type x>
void foo<T>::bar(){}
This compiles fine (gcc / clang)
An attempt to answer the original question (by Darhuuk, slightly modified) was this:
// C
#include <type_traits>
template <typename T> struct length { using type = unsigned int; };
template <typename T> using length_t = typename length<T>::type;
template <typename type>
class Object {
template <length_t<Object<type>> length>
void put(type (&)[length]);
};
template <typename type>
template <length_t<Object<type>> length>
void Object<type>::put(type (&)[length]) {}
int main() {}
Clang seems to have similar problems as with the original code and emits the error:
<source>:15:20: error: out-of-line definition of 'put' does not match any declaration in 'Object<type>'
void Object<type>::put(type (&)[length]) {}
^~~
1 error generated.
while gcc compiles it without complaints.
Who is right about C? Is it a bug in clang or is gcc being lax?
Why does A not compile while B does?
as I mentioned in
Template class type alias failing substitution in member declaration:
CWG2, the ancient issue that nobody knows when it's posted, is still drafting, which means the match rule of out-of-definition is even unspecified. these weird mismatches are because of the different implementations of compilers.

Templated function on templated subclass [duplicate]

This question already has answers here:
Workaround for template argument deduction in non-deduced context
(1 answer)
Nested template and parameter deducing [duplicate]
(1 answer)
Closed 8 years ago.
I'm having a bit of trouble trying to figure out why gcc cannot deduce the template arguments in the following code:
template <int N>
struct A {
template <int M>
struct B {
};
};
template <int N, int M>
void function(typename A<N>::template B<M> &b) {
// do stuff
}
int main() {
A<1>::B<2> b;
function(b);
}
Error:
$ g++ -std=c++11 -std=gnu++11 test.cpp
test.cpp: In function ‘int main()’:
test.cpp:19:12: error: no matching function for call to ‘function(A<1>::B<2>&)’
function(b);
^
test.cpp:19:12: note: candidate is:
test.cpp:13:6: note: template<int N, int M> void function(typename A<N>::B<M>&)
void function(typename A<N>::template B<M> &b) {
^
test.cpp:13:6: note: template argument deduction/substitution failed:
test.cpp:19:12: note: couldn't deduce template parameter ‘N’
function(b);
I leave this answer mainly because of the comments. The question has apparently been previously posted and answered.
But as a rule of thumb, for template deduction, in my experience use clang as replacement/alternative, it has much better error messages for such issues (like why template deduction failed).