I have simplified my somewhat more difficult problem to this:
http://coliru.stacked-crooked.com/a/2660b33492651e92
#include <iostream>
#include <string>
#include <type_traits>
template<typename C>
struct get_type
{
C operator()() const = delete;
};
template<>
struct get_type<std::string>
{
std::string operator()() const { return "asd"; }
};
template<>
struct get_type<size_t> {
size_t operator()() const { return 6; }
};
struct S
{
S(){}
template<typename T>
operator T() { return get_type<T>{}(); }
};
struct A
{
A() :s{S{}}, n{S{}} {}
std::string s;
size_t n;
};
int main()
{
A a;
std::cout << "Spock out." << std::endl;
}
This generates the following error:
'In instantiation of 'S::operator T() [with T = char]':'...
Why is T is deduced to char and not std::string?
Edit:
@YSC's answer seems to be correct: https://stackoverflow.com/a/46608866/4723722
I edited the post to add a solution: http://coliru.stacked-crooked.com/a/06d31d981acd2544
struct S
{
S(){}
template<typename T>
explicit operator T() { return get_type<T>{}(); }
};
Here:
A() : s( S{} ), ...
the construction of A::s
from a S
instance is ambiguous because for every type T
for witch std::is_constructible<std::string, T>
, the template function S::operator T()
is a possible conversion path from S
to T
to std::string
.
It seams your version of GCC starts by testing with T
= char
. clang list multiple candidates: http://coliru.stacked-crooked.com/a/17e247cca8b79c77:
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/basic_string.h:413:7: note: candidate constructor basic_string(const _Alloc& __a) _GLIBCXX_NOEXCEPT ^ /usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/basic_string.h:421:7: note: candidate constructor basic_string(const basic_string& __str) ^ /usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/basic_string.h:493:7: note: candidate constructor basic_string(const _CharT* __s, const _Alloc& __a = _Alloc()) ^ /usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/basic_string.h:515:7: note: candidate constructor basic_string(basic_string&& __str) noexcept ^ /usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/basic_string.h:542:7: note: candidate constructor basic_string(initializer_list<_CharT> __l, const _Alloc& __a = _Alloc())
As OP found by themself, making the conversion operator of S
explicit
solves the ambiguity (demo):
struct S
{
S(){}
template<typename T>
explicit operator T() { return get_type<T>{}(); }
};
And this works (as user AndyG found) because under [over.match.copy] one can read:
When initializing a temporary to be bound to the first parameter of a constructor where the parameter is of type “reference to possibly cv-qualified T” and the constructor is called with a single argument in the context of direct-initialization of an object of type “cv2 T”, explicit conversion functions are also considered.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With