I would like to SFINAE-disable a class template constructor - but the catch is that an enable-disable condition is a trait of that class. Particularly, I would like to enable that constructor only if a specific class method, matching constructor's template arguments, exists in that class.
A simplified example:
struct S
{
std::size_t n = 0;
void set_value(int i) { n = i; }
void set_value(const std::string & s) { n = s.size(); }
template <class T, class = decltype(std::declval<S>().set_value(std::declval<T>()))>
S(T && x)
{
set_value(std::forward<T>(x));
}
};
That solution works in GCC and, allegedly, in MSVC, but does not work in Clang. And in my opinion Clang is in the right here, because inside a class definition class isn't yet defined, so std::declval<S>()
should not work here.
So my idea was to move that constructor definition out of class definition:
struct S
{
std::size_t n = 0;
void set_value(int i) { n = i; }
void set_value(const std::string & s) { n = s.size(); }
template <class T, class>
S(T &&);
};
template <class T, class = decltype(std::declval<S>().set_value(std::declval<T>()))>
S::S(T && x)
{
set_value(std::forward<T>(x));
}
But that doesn't work in GCC and MSVC. That solution also looks somewhat fishy, because I first declare that constructor unconditionally and only later introduce SFINAE.
My question is - is it possible at all to solve that task in C++11/14/17? A specific task is that a class should have a constructor only if it also have a matching setter method. Obviously, one can manually define a non-template constructor for every setter method overload, but that tedious and hard to maintain.
But I'm also interested in more general task - SFINAE-enabling a constructor or other class method by a trait of that class (requiring a complete class definition).
Also, which compiler is more right in my example - GCC/MSVC or Clang?