I use this library of options: https://github.com/cbeck88/strict-variant . It provides a class similar to std::variant and boost::variant . Given this struct :
struct S { explicit S(double) {} };
I want to do this:
strict_variant::variant<double, S> v = 2.0;
This works with Clang 5.0.1 and MSVC 12.19.25831.00, but does not compile with GCC 7.2.1.
I looked at the library code and reduced the problem to this:
#include <iostream> struct S { constexpr S() {} constexpr explicit S(double) {} }; template<unsigned i> struct init_helper; template<> struct init_helper<0> { using type = double; }; template<> struct init_helper<1> { using type = S; }; template<unsigned i> struct initializer_leaf { using target_type = typename init_helper<i>::type; constexpr unsigned operator()(target_type) const { return i; } }; struct initializer : initializer_leaf<0>, initializer_leaf<1> { }; int main() { std::cout << initializer()(double{}) << " = double" << '\n'; std::cout << initializer()(S{}) << " = S" << '\n'; return 0; }
with exit
0 = double 1 = S
GCC says:
strict_variant_test.cpp: In function 'int main()': strict_variant_test.cpp:29:37: error: request for member 'operator()' is ambiguous std::cout << initializer()(double{}) << " = double" << '\n'; ^ strict_variant_test.cpp:17:21: note: candidates are: constexpr unsigned int initializer_leaf<i>::operator()(initializer_leaf<i>::target_type) const [with unsigned int i = 1; initializer_leaf<i>::target_type = S] constexpr unsigned operator()(target_type) const ^~~~~~~~ strict_variant_test.cpp:17:21: note: constexpr unsigned int initializer_leaf<i>::operator()(initializer_leaf<i>::target_type) const [with unsigned int i = 0; initializer_leaf<i>::target_type = double] strict_variant_test.cpp:30:32: error: request for member 'operator()' is ambiguous std::cout << initializer()(S{}) << " = S" << '\n'; ^ strict_variant_test.cpp:17:21: note: candidates are: constexpr unsigned int initializer_leaf<i>::operator()(initializer_leaf<i>::target_type) const [with unsigned int i = 1; initializer_leaf<i>::target_type = S] constexpr unsigned operator()(target_type) const ^~~~~~~~ strict_variant_test.cpp:17:21: note: constexpr unsigned int initializer_leaf<i>::operator()(initializer_leaf<i>::target_type) const [with unsigned int i = 0; initializer_leaf<i>::target_type = double]
But it works with GCC (and still Clang and MSVC) when I change the initializer definition to this:
struct initializer { constexpr unsigned operator()(double) const { return 0; } constexpr unsigned operator()(S) const { return 1; } };
My understanding of C ++ says this is equivalent, so I assume it is a bug in GCC, but I often run into problems when the standard says amazing things and my assumption is wrong. So my question is: whose fault is this? GCC has an error, Clang and MSVC have an error, or is this an interpretation of undefined / unspecified code, so are all compilers right? If the code is incorrect, how can I fix it?
c ++ g ++ clang ++ c ++ 17
Mr. Metric
source share