【问题标题】:Checking member function overload existence from a template从模板检查成员函数重载是否存在
【发布时间】:2018-11-23 08:18:42
【问题描述】:

是否可以从模板成员函数中检查一个类是否有某个成员函数重载?

我能找到的最好的类似问题是:Is it possible to write a template to check for a function's existence? 据我了解,这不适用于检查函数重载的情况。

这是一个如何应用的简化示例:

struct A;
struct B;

class C
{
public:
    template<typename T>
    void doSomething(std::string asdf)
    {
        T data_structure;

        /** some code */

        if(OVERLOAD_EXISTS(manipulateStruct, T))
        {
            manipulateStruct(data_structure);
        }

        /** some more code */
    }

private:
    void manipulateStruct(B& b) {/** some different code */};
}

我的问题是,是否存在某种标准方法可以使以下代码的用法起作用:

int main(int argc, const char** argv)
{
    C object;
    object.doSomething<A>("hello");
    object.doSomething<B>("world");
    exit(0);
}

我能想到的唯一方法是简单地为结构A 创建一个manipulateStruct 的空重载。否则,操作方法当然也可以放入要操作的结构中,这将使 SFINAE 成为一种选择。让我们假设这两种情况都不存在。

有什么方法可以让与上述类似的代码工作吗?是否存在类似于OVERLOAD_EXISTS 的内容,让编译器知道何时将manipulateStruct 部分添加到生成的代码中?或者是否有一些聪明的方法可以让 SFINAE 为这种情况工作?

【问题讨论】:

    标签: c++ templates sfinae


    【解决方案1】:

    测试是否存在重载 (C++11)

    从 C++11 开始,您可以混合使用 std::declvaldecltype 来测试是否存在特定的重载:

    // If overload exists, gets its return type.
    // Else compiler error
    decltype(std::declval<C&>().manipulateStruct(std::declval<T&>()))
    

    这可以在 SFINAE 构造中使用:

    class C {
    public:
        // implementation skipped
    private:
        // Declared inside class C to access its private member.
        // Enable is just a fake argument to do SFINAE in specializations.
        template<typename T, typename Enable=void>
        struct can_manipulate;
    }
    
    template<typename T, typename Enable>
    struct C::can_manipulate : std::false_type {};
    
    // Implemented outside class C, because a complete definition of C is needed for the declval.
    template<typename T>
    struct C::can_manipulate<T,std::void_t<decltype(std::declval<C&>().manipulateStruct(std::declval<T&>()))>> : std::true_type {};
    

    这里我忽略了使用std::void_t 的重载的返回类型(C++17,但 C++11 的替代方案应该是可能的)。如果要检查返回类型,可以将其传递给std::is_samestd::is_assignable

    doSomething 实现

    C++17

    这可以通过constexpr if来完成:

    template<typename T>
    void doSomething(std::string asdf) {
        T data_structure;
        if constexpr (can_manipulate<T>::value) {
            manipulateStruct(data_structure);
        }
    }
    

    如果条件评估为假,if constexpr 将使编译器丢弃statement-true。如果没有constexpr,编译将要求if 内的函数调用在所有情况下都有效。

    Live demo (C++17 full code)

    C++11

    您可以使用 SFINAE 模拟 if constexpr 行为:

    class C {
        // previous implementation
    private:
        template<typename T, typename Enable=void>
        struct manipulator;
    }
    
    template<typename T, typename Enable>
    struct C::manipulator {
        static void call(C&, T&) {
            //no-op
        }
    };
    
    // can_manipulate can be inlined and removed from the code
    template<typename T>
    struct C::manipulator<T, typename std::enable_if<C::can_manipulate<T>::value>::type> {
        static void call(C& object, T& local) {
            object.manipulateStruct(local);
        }
    };
    

    函数体:

    template<typename T>
    T doSomething()
    {
        T data_structure;
        // replace if-constexpr:
        manipulator<T>::call(*this, data_structure);
    }
    

    Live demo (C++11 full code)

    【讨论】:

    • 感谢您的回复!遗憾的是,我目前正在使用 C++11,但是在 C++17 下使用 if constexpr 可以实现这种类型的代码仍然非常酷
    • @abcalphabet 我为 C++11 添加了一个(详细的)解决方法。
    • 完美,C++11 解决方法对我有用,非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-04
    • 1970-01-01
    • 1970-01-01
    • 2015-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多