如何检查 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; 
}

Here's the above example in compiler explorer.

不幸的是,我相信您必须制作自己的版本,将 {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。一定不要假设它按原样完美运行。