#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)
}
Таким образом, код A
может быть создан только с аргументом int
.
make_a
использует SFINAE, поэтому экземпляр make_a<Args...>
создается только тогда, когда A::A(Args...)
существует. Это делается с помощью decltype()
в возвращаемом типе, где args
доступен.
Можно ли аналогичным образом ограничить шаблонный конструктор B::B<Args...>
? Здесь выражение, запускающее SFINAE, может находиться только в аргументах шаблона.
В C++20 вы можете сделать это с помощью ограничения и выражения require:
struct B {
B(const auto&... args) requires(requires { A(args...); }) : a(args...) { }
A a;
};
Или, если вы не хотите использовать спецификатор noException, у вас не будет доступа к args
, и вместо него вы можете использовать std::declval<const Args&>()
:
template<typename... Args,
decltype(static_cast<void>(A(std::declval<const Args&>()...)), nullptr) = nullptr>
B(const Args&... args) : a(args...) { }
Это неправильно. Спецификатор noException объявления шаблона функции не участвует в SFINAE. Он создается только после того, как функция выбрана путем разрешения перегрузки, и в этот момент это серьезная ошибка, если она содержит недопустимое выражение. См. eel.is/c++draft/temp.deduct.general#7
@BrianBi Вы правы. Я думал о noexcept(noexcept(...)) -> decltype(...)
и думал, что noexcept
делает замену неудачной, но это был decltype
.
Я сейчас использую
class = std::void_t<decltype(A(std::declval<Args>()...))>