如何防止具有不同参数类型的 std::function 之间的隐式转换?

cod*_*704 7 c++ implicit-conversion std-function

我正在尝试绑定一些ta-lib函数,然后回调。

这是简化的示例代码:

#include <functional>
#include <type_traits>
#include <cstdint>

struct DataChunk {
    // ...
};
typedef uint64_t idx_t;

template <typename LookbackArgType> // int, double
struct talib_traits {
    using talib_lookback_t = std::function<int(LookbackArgType)>;
    using talib_function_t = std::function<void(DataChunk &, void *, idx_t, idx_t, LookbackArgType)>;
};

template <>
struct talib_traits<void> {
    using talib_lookback_t = std::function<int()>;
    using talib_function_t = std::function<void(DataChunk &, void *, idx_t, idx_t)>;
};

struct X {
    talib_traits<int>::talib_lookback_t talib_lookback_int = nullptr;
    talib_traits<int>::talib_function_t talib_function_int = nullptr;

    talib_traits<double>::talib_lookback_t talib_lookback_double = nullptr;
    talib_traits<double>::talib_function_t talib_function_double = nullptr;

    talib_traits<void>::talib_lookback_t talib_lookback_void = nullptr;
    talib_traits<void>::talib_function_t talib_function_void = nullptr;

    explicit X(talib_traits<int>::talib_lookback_t talib_lookback, talib_traits<int>::talib_function_t talib_function)
        : talib_lookback_int(talib_lookback), talib_function_int(talib_function) {
    }
    explicit X(talib_traits<double>::talib_lookback_t talib_lookback,
               talib_traits<double>::talib_function_t talib_function)
        : talib_lookback_double(talib_lookback), talib_function_double(talib_function) {
    }
    explicit X(talib_traits<void>::talib_lookback_t talib_lookback, talib_traits<void>::talib_function_t talib_function)
        : talib_lookback_void(talib_lookback), talib_function_void(talib_function) {
    }
};

int main() {
    constexpr bool lookback_is_same =
        std::is_same<talib_traits<int>::talib_lookback_t, talib_traits<double>::talib_lookback_t>::value;
    constexpr bool function_is_same =
        std::is_same<talib_traits<int>::talib_function_t, talib_traits<double>::talib_function_t>::value;
    static_assert(!lookback_is_same && !function_is_same);

    X x([](void) { return 0; }, [](DataChunk &, void *, idx_t, idx_t) {}); // okay

    // ambiguous: more than one instance of constructor "X::X" matches the argument list, int or double?
    X y([](int) { return 0; }, [](DataChunk &, void *, idx_t, idx_t, int) {});
}
Run Code Online (Sandbox Code Playgroud)

我怎样才能使它们明确,即防止std::function<int(int)>/int (*)(int)转换为std::function<int(double)>

我尝试explicit在构造函数中添加关键字前缀,但仍然无法防止歧义。

Chr*_*ial 7

我不认为你可以阻止std::function<int(double)>int(int)lambda 构造 a 。这就是std::function定义的 \xe2\x80\x93 它可以从任何可使用 std::functions\ 的 arg 类型调用的可调用对象构造,其返回类型可隐式转换为所需的返回类型。

\n

您可以通过在调用站点将 lambda 转换为 std::function 来避免这种情况。在这种情况下,您不需要为 std::function 指定类型参数:

\n
X y(std::function([](int) { return 0; }),\n    [](DataChunk &, void *, idx_t, idx_t, int) {});\n
Run Code Online (Sandbox Code Playgroud)\n

现在,所采用的构造函数std::function<int(int)>不再需要第一个参数的隐式转换,而std::function<int(double)>变体仍然需要。这足以让通话不再含糊不清。

\n

人们希望将 lambda 转换为 astd::function<int(double)>与 a时所需的返回类型的额外转换std::function<int(int)>已经足以成为决胜局。但是这两个转换都使用相同的构造函数std::function,并且类型转换发生在该构造函数内。平局打破算法不会查看构造函数内部,因此两种情况对它来说看起来都是一样的。

\n
\n

或者,如果将构造函数转换为模板化工厂函数,则可以显式指定类型 ( godbolt ):

\n
X y(std::function([](int) { return 0; }),\n    [](DataChunk &, void *, idx_t, idx_t, int) {});\n
Run Code Online (Sandbox Code Playgroud)\n


Aco*_*gua 5

让隐式std::function构造适当的对象显然会失败 \xe2\x80\x93 替代方案(显式构造对象或工厂函数)在用户端(即构造对象时)std::function有点冗长。如果您希望在那里提供更多舒适感,则需要在实施上投入更多精力;一种可能的方法是使构造函数成为接受任意类型的模板,并应用静态类型检查以确定是否已传递正确的参数。这可能如下所示。首先,我们需要一些进一步的特征来确定函数传递给实际接受的参数:X

\n
template <typename T>\nstruct FunctionTraits;\ntemplate <typename T>\nstruct FunctionTraits<std::function<int(T)>>\n{\n    using ParameterType = T;\n};\ntemplate <typename T>\nstruct FunctionTraits<std::function<void(DataChunk &, void *, idx_t, idx_t, T)>>\n{\n    using ParameterType = T;\n};\ntemplate <>\nstruct FunctionTraits<std::function<int()>>\n{\n    using ParameterType = void;\n};\ntemplate <>\nstruct FunctionTraits<std::function<void(DataChunk &, void *, idx_t, idx_t)>>\n{\n    using ParameterType = void;\n};\ntemplate <typename T>\nusing ParameterType\n= typename FunctionTraits<decltype(std::function(std::declval<T>()))>::ParameterType;\n\n
Run Code Online (Sandbox Code Playgroud)\n

现在我们可以在模板构造函数中使用它们:

\n
struct X\n{\n   // ...\n\n    template <typename Lookback, typename Function>\n    X(Lookback l, Function f)\n    {\n        using PType = ParameterType<decltype(l)>;\n        static_assert(std::is_same_v<PType, ParameterType<decltype(f)>>);\n        if constexpr(std::is_same_v<PType, int>)\n        {\n            talib_lookback_int = l;\n            talib_function_int = f;\n        }\n        else if constexpr(std::is_same_v<PType, double>)\n        {\n            talib_lookback_double = l;\n            talib_function_double = f;\n        }\n        else if constexpr(std::is_same_v<PType, void>)\n        {\n            talib_lookback_void = l;\n            talib_function_void = f;\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

如果传递不匹配的函数(与相关类型ParamType的签名不同或什至不匹配),代码将失败std::function,否则通过普通构造函数调用优雅地创建预期的对象,请参阅godbolt上的演示。

\n