【问题标题】:C++ template match type without duplicate definition没有重复定义的 C++ 模板匹配类型
【发布时间】:2018-05-13 22:06:01
【问题描述】:

我正在围绕 std::function 创建一个模板包装器。为了匹配返回 void 的函数,我将 std::monostate 作为我的 Unit 类型。我一直在尝试为我的函数包装类型创建两个可变参数模板,一个

template<Unit, Args...>

还有一个

template<T, Args...> where T != Unit.

这是我使用 enable_if 得到的最接近的结果。

#include <functional>
#include <variant>

namespace func {
    // Declare unit type
    typedef std::monostate Unit;

    // Empty type, maybe use this instead of monostate
    template<typename ... ts>
    struct Term {};

    // Variables
    template<typename T>
    struct Term<T>
    {
        T val;
        // Constructor
        Term(T in): val(in) {}

        // Call op just returns the value
        T operator() () const {
            return this->val;
        }
    };

    // Functions that return void
    template <typename T,
                typename ... Args,
                typename = std::enable_if_t<std::is_same<T, Unit>::value>>
    struct Term<Args...>
    {
        // Void returning function
        const std::function<void(Args...)> body;

        // Void returning constructor
        Term(std::function<void(Args...)> func): body(func) {}

        // Void function Caller
        void operator() (const Args&& ...a) const {
            this->body(std::forward<Args>(a)...);
        }
    };

    // Functions that return T
    template <typename T,
                typename ... Args,
                typename = std::enable_if_t<!std::is_same<T, Unit>::value>>
    struct Term<T, Args...>
    {
        // T returning function
        const std::function<T(Args...)> body;

        // T returning constructor
        Term(std::function<T(Args...)> func): body(func) {}

        // T returning function Caller
        T operator() (const Args&& ...a) const {
            return this->body(std::forward<Args>(a)...);
        }
    };

}

但是,在第一种情况下,我遇到了关于不可演绎参数 T 的错误。但是,我已经通过我的enable_if 模板参数知道该参数将是Unit 类型。如何让编译器同时接受这两个定义?

错误

$ clang++ -std=c++17 terms.hpp -pedantic
terms.hpp:29:18: error: default template argument in a class template partial specialization
                                typename = std::enable_if_t<std::is_same<T, Unit>::value>>
                                           ^
terms.hpp:30:9: error: class template partial specialization contains template parameters that cannot be deduced; this partial specialization will never be used [-Wunusable-partial-specialization]
        struct Term<Args...>
               ^~~~~~~~~~~~~
terms.hpp:27:21: note: non-deducible template parameter 'T'
        template <typename T,
                           ^
terms.hpp:29:7: note: non-deducible template parameter (anonymous)
                                typename = std::enable_if_t<std::is_same<T, Unit>::value>>
                                ^
terms.hpp:47:18: error: default template argument in a class template partial specialization
                                typename = std::enable_if_t<!std::is_same<T, Unit>::value>>
                                           ^
terms.hpp:48:9: error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used [-Wunusable-partial-specialization]
        struct Term<T, Args...>
               ^~~~~~~~~~~~~~~~
terms.hpp:47:7: note: non-deducible template parameter (anonymous)
                                typename = std::enable_if_t<!std::is_same<T, Unit>::value>>
                                ^
4 errors generated.

编辑:应该这样使用

auto c = Term<int>(42);
auto fun = Term<int, int, int>([](int a, int b) { return a + b; });

std::cout << c() << std::endl;
std::cout << fun(3,4) << std::endl;

【问题讨论】:

  • 我相信你应该把匿名的enable_if参数放在Args之后,而不是之前。
  • @DanielH 很酷,感谢我编辑将 Args 放在匿名模板参数之前。虽然仍然出现同样的错误,所以它可能还没有出现。
  • 你打算如何实例化这个模板?

标签: c++ types metaprogramming


【解决方案1】:

您目前的模板专业化问题是struct Term&lt;Args...&gt;struct Term&lt;T, Args...&gt; 不是互斥的,可以匹配相同的东西。所以我建议你把这两种情况合二为一,SFINAE 上的重载调用算子。

这种方法当然有一些限制,但这些限制只是继承自您的原始方法。例如,不可能包装一个不接受参数但返回一个值(即与标量不明确)的函数。

#include <functional>
#include <iostream>
#include <variant>

namespace func {
    // Declare unit type
    typedef std::monostate Unit;

    // Empty type, maybe use this instead of monostate
    template<typename...>
    struct Term;

    // Variables
    template<typename T>
    struct Term<T>
    {
        T val;
        // Constructor
        Term(T in): val(in) {}

        // Call op just returns the value
        T operator() () const {
            return this->val;
        }
    };

    // Functions that return void
    template <typename T, typename ... Args>
    struct Term<T, Args...>
    {
        using R = typename std::conditional<std::is_same<T, Unit>::value,void,T>::type;
        // Void returning function
        const std::function<R(Args...)> body;

        // Void returning constructor
        Term(std::function<R(Args...)> func): body(func) {}

        // Void function Caller
        template <typename U = R>
        typename std::enable_if<std::is_same<U, Unit>::value,void>::type
        operator() (Args&& ...a) const {
            this->body(std::forward<Args>(a)...);
        }

        // T returning function Caller
        template <typename U = R>
        typename std::enable_if<!std::is_same<U, Unit>::value,T>::type
        operator() (Args&& ...a) const {
            return this->body(std::forward<Args>(a)...);
        }

    };
}

int main() {
    auto c = func::Term<int>(42);
    auto fun = func::Term<int, int, int>([](int a, int b) { return a + b; });

    std::cout << c() << std::endl;
    std::cout << fun(3,4) << std::endl;
}

Live example

【讨论】:

  • 太棒了,我不知道 std::conditional 并且我不知道你可以嵌套这样的模板,以解决“部分专业化中不允许默认模板参数”的问题提及。最后,对于“不可能包装不带参数但返回值的函数”,我可以将它们声明为变量,例如我在main 中为c() 所做的事情。再次感谢!
  • 如果我也想将可变参数 Args... 也翻译成 void,就像你为 R 所做的那样?
  • @rausted 为什么要将Args... 翻译成void?这甚至是不可能的,即函数签名,例如int(int,void,double) 不是有效的 C++。唯一有效的情况是int(void),其中void 是唯一的参数,并且由常量情况涵盖,正如您在之前的评论中所推理的那样。
猜你喜欢
  • 1970-01-01
  • 2011-06-27
  • 2010-10-25
  • 2011-02-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多