#include <string>
#include <type_traits>
struct A {
A(int) {}
};
template<typename... Args>
auto make_A(const Args&... args) -> decltype(A(args...)) {
return A(args...);
}
struct B {
template<typename... Args
//std::enable_if ?
>
B(const Args&... args) : a(args...) { }
A a;
};
int main() {
A a1 = make_a(123);
//A a2 = make_a(std::string("123")); // no make_a<std::string>
B b1(123); // ok
B b2(std::string("123")); // fails because A(std::string) does not exist,
// but should fail already because there is no B(std::string)
}
In thus code A
can only be constructed with an int
argument.
make_a
uses SFINAE, so that make_a<Args...>
only gets instantiated when A::A(Args...)
exists. It does this using decltype()
in the return type, where args
is available.
Is it also possible to restrict the templated constructor B::B<Args...>
in a similar way? Here the SFINAE-triggering expression can only be in the template arguments.
In C++20 you can do it with a constraint and a requires expression:
struct B {
B(const auto&... args) requires(requires { A(args...); }) : a(args...) { }
A a;
};
Or if you don't want to use the noexcept specifier, you won't have access to args
, and you can use std::declval<const Args&>()
instead:
template<typename... Args,
decltype(static_cast<void>(A(std::declval<const Args&>()...)), nullptr) = nullptr>
B(const Args&... args) : a(args...) { }