如何检查 class 是否可以从多个参数显式构造?
How to check if a class if explicitly constructible from multiple arguments?
有没有一个好的方法来评估存在多个参数的显式构造函数?这与 非常相似,除了 std::is_convertible
不适用于这种情况,因为我们有多个参数被传递给我们正在测试的构造函数。
例如:
#include <iostream>
#include <type_traits>
struct InitParams
{
int Parameter1;
int Parameter2;
};
class ExampleFloatConstructible
{
public:
explicit ExampleFloatConstructible(float InValue, const InitParams& InParams);
};
class ExampleIntConstructible
{
public:
explicit ExampleIntConstructible(int InValue, const InitParams& InParams);
};
template<typename ClassToTest, typename ArgType>
struct IsExplicitlyConstructibleWithSettings
{
static constexpr bool value = std::is_constructible<ClassToTest, ArgType, const InitParams&>::value;
};
int main()
{
// "Correct" values:
// will be true:
std::cout << "ExampleFloatConstructible Can Be Built from float? " <<
IsExplicitlyConstructibleWithSettings<ExampleFloatConstructible, float>::value << std::endl;
// will be true:
std::cout << "ExampleIntConstructible Can Be Built from int? " <<
IsExplicitlyConstructibleWithSettings<ExampleIntConstructible, int>::value << std::endl;
// "Incorrect" values:
// will also be true, because int is convertible from float
std::cout << "ExampleIntConstructible Can Be Built from float? " <<
IsExplicitlyConstructibleWithSettings<ExampleIntConstructible, float>::value << std::endl;
// will also be true, because float is convertible from int
std::cout << "ExampleFloatConstructible Can Be Built from int? " <<
IsExplicitlyConstructibleWithSettings<ExampleFloatConstructible, int>::value << std::endl;
}
不幸的是,我相信您必须制作自己的版本,将 {args}
传递给假装函数而不是 arg
(live example):
// Can we call this function with {args}?
template<typename To>
void conversion_test(To);
// If passing {From...} as an arg to conversion_test<To> fails, return false.
template<typename To, typename... From>
constexpr bool sfinae_helper(...) {
return false;
}
// If passing {From...} as an arg to conversion_test<To> succeeds, beat the ellipsis and return true.
template<typename To, typename... From>
constexpr auto sfinae_helper(int)
-> decltype(conversion_test<To>({std::declval<From>()...}), true) {
return true;
}
// Fall back to the standard trait when possible. You might want to make this name a struct for consistency.
template<typename To, typename... From>
constexpr bool is_multi_convertible_to() {
if constexpr (sizeof...(From) == 1) {
return std::is_convertible_v<From..., To>;
} else {
return sfinae_helper<To, From...>(0);
}
}
template<typename To, typename... From>
constexpr bool is_multi_convertible_to_v = is_multi_convertible_to<To, From...>();
struct has_implicit {
has_implicit(int, int) {}
};
struct has_explicit {
explicit has_explicit(int, int) {}
};
// Warning: Not a comprehensive test suite!
static_assert(is_multi_convertible_to_v<int, double>);
static_assert(not is_multi_convertible_to_v<char*, const char*>);
static_assert(is_multi_convertible_to_v<has_implicit, int, int>);
static_assert(not is_multi_convertible_to_v<has_explicit, int, int>);
值得注意的是,Concepts 会删除 sfinae_helper
层,但这应该适用于 C++17。一定不要假设它按原样完美运行。
有没有一个好的方法来评估存在多个参数的显式构造函数?这与 std::is_convertible
不适用于这种情况,因为我们有多个参数被传递给我们正在测试的构造函数。
例如:
#include <iostream>
#include <type_traits>
struct InitParams
{
int Parameter1;
int Parameter2;
};
class ExampleFloatConstructible
{
public:
explicit ExampleFloatConstructible(float InValue, const InitParams& InParams);
};
class ExampleIntConstructible
{
public:
explicit ExampleIntConstructible(int InValue, const InitParams& InParams);
};
template<typename ClassToTest, typename ArgType>
struct IsExplicitlyConstructibleWithSettings
{
static constexpr bool value = std::is_constructible<ClassToTest, ArgType, const InitParams&>::value;
};
int main()
{
// "Correct" values:
// will be true:
std::cout << "ExampleFloatConstructible Can Be Built from float? " <<
IsExplicitlyConstructibleWithSettings<ExampleFloatConstructible, float>::value << std::endl;
// will be true:
std::cout << "ExampleIntConstructible Can Be Built from int? " <<
IsExplicitlyConstructibleWithSettings<ExampleIntConstructible, int>::value << std::endl;
// "Incorrect" values:
// will also be true, because int is convertible from float
std::cout << "ExampleIntConstructible Can Be Built from float? " <<
IsExplicitlyConstructibleWithSettings<ExampleIntConstructible, float>::value << std::endl;
// will also be true, because float is convertible from int
std::cout << "ExampleFloatConstructible Can Be Built from int? " <<
IsExplicitlyConstructibleWithSettings<ExampleFloatConstructible, int>::value << std::endl;
}
不幸的是,我相信您必须制作自己的版本,将 {args}
传递给假装函数而不是 arg
(live example):
// Can we call this function with {args}?
template<typename To>
void conversion_test(To);
// If passing {From...} as an arg to conversion_test<To> fails, return false.
template<typename To, typename... From>
constexpr bool sfinae_helper(...) {
return false;
}
// If passing {From...} as an arg to conversion_test<To> succeeds, beat the ellipsis and return true.
template<typename To, typename... From>
constexpr auto sfinae_helper(int)
-> decltype(conversion_test<To>({std::declval<From>()...}), true) {
return true;
}
// Fall back to the standard trait when possible. You might want to make this name a struct for consistency.
template<typename To, typename... From>
constexpr bool is_multi_convertible_to() {
if constexpr (sizeof...(From) == 1) {
return std::is_convertible_v<From..., To>;
} else {
return sfinae_helper<To, From...>(0);
}
}
template<typename To, typename... From>
constexpr bool is_multi_convertible_to_v = is_multi_convertible_to<To, From...>();
struct has_implicit {
has_implicit(int, int) {}
};
struct has_explicit {
explicit has_explicit(int, int) {}
};
// Warning: Not a comprehensive test suite!
static_assert(is_multi_convertible_to_v<int, double>);
static_assert(not is_multi_convertible_to_v<char*, const char*>);
static_assert(is_multi_convertible_to_v<has_implicit, int, int>);
static_assert(not is_multi_convertible_to_v<has_explicit, int, int>);
值得注意的是,Concepts 会删除 sfinae_helper
层,但这应该适用于 C++17。一定不要假设它按原样完美运行。