独立于模板参数位置

时间:2016-05-11 08:45:39

标签: c++ templates c++14 c++17

我遇到了需要一个具有相同输出的模板函数的问题,只要它的模板参数是相同的,与位置无关。鉴于总有两个参数。

我有一个功能:

template<typename Lhs, typename Rhs>
int func();

我希望func<int, float>()func<float, int>()调用相同的代码。

我想到了一个我想避免使用的宏,但是当两种类型相同时我不需要重复代码。所以宏如:

#define DEF_TEMPL_IMPL(lhs, rhs, ret) \
template<>\
auto func<lhs, rhs>(){ return ret; }\
template<>\
auto func<rhs, lhs>(){ return func<lhs, rhs>(); }

将无法编译,因为DEF_TEMPL_IMPL(float, float, 3)会导致重新定义func<>

我认为SFINAE就是答案,但不能想到解决方案。

我会继续思考这个问题,但是在回答这个问题之前,堆栈溢出的一些优秀思想可能会比我能提出的更好或更优雅。

那怎么能实现呢?

3 个答案:

答案 0 :(得分:9)

您可以为每个类型对编写一个专门化,然后在调用它时将主模板委托给func<Rhs,Lhs>()

//This will get called if no specializations match
template<typename Lhs, typename Rhs>
int func() {
    //Check if there is a specialization for the flipped pair
    return func<Rhs, Lhs>();   
}

//This will get called for <int,float> and <float,int>
template <>
int func<int,float>() {
    return 42;   
}

//Ditto for <bool,float> and <float,bool>
template <>
int func<bool,float>() {
    return 0xbeef;   
}

//Specializations with the same arguments are also supported by this scheme
template <>
int func<bool,bool>() {
    return 12;   
}

Live Demo

答案 1 :(得分:3)

这是另一种可能的解决方案

template<typename A, typename B>
struct List { };

template<typename A, typename B>
struct Set { 
   Set(List<A, B>) { }
   Set(List<B, A>) { }
};

template<typename A>
struct Set<A, A> { 
   Set(List<A, A>) { }
};

现在,将每个写为Set<A, B>的重载,就像这样

template<typename Lhs, typename Rhs>
int func() {
    return func(List<Lhs, Rhs>());
}

int func(Set<int, float>) {
    return 42;   
}

int func(Set<bool, float>) {
    return 0xbeef;   
}

int func(Set<bool, bool>) {
    return 12;   
}

这样,您还可以定义func(List<float, int>),如果由于某种原因,float, int的顺序很重要。如果您不小心定义了Set<int,float>Set<float, int>,则调用将是不明确的。

您还可以通过更改二进制文件Set<A, B>的定义,在定义重载时验证您没有重复Set<B, A> vs Set的重载。到这个

template<typename A, typename B>
struct Set { 
   Set(List<A, B>) { }
   Set(List<B, A>) { }

   friend void dupeCheck(List<A, B>) { }
   friend void dupeCheck(List<B, A>) { }
};
然而,对于这个技巧,这并不是绝对必要的。特别是因为JIT编译器中的类型可以以一致的方式轻松地手动排序。

答案 2 :(得分:1)

您可以定义类型的顺序(例如int - 1,float - 2,char - 3)。 然后实现一个将这两种类型排序的函数:

#include <iostream>

template<typename T>
struct Order;

// There should be a better solution for getting type id in compile time
// This will let you specify the order
template<>
struct Order<int>
{
    enum { Value = 1 };
};

template<>
struct Order<float>
{
    enum { Value = 2 };
};

template<>
struct Order<char>
{
    enum { Value = 3 };
};

template<typename A, typename B, bool swap>
struct Sort;

template<typename A, typename B>
struct Sort<A, B, false>
{
    typedef A First;
    typedef B Second;
};

template<typename A, typename B>
struct Sort<A, B, true>
{
    typedef B First;
    typedef A Second;
};


template<typename Lhs, typename Rhs>
int func_sorted()
{
    return 1;
}

template<>
int func_sorted<int, float>()
{
    return 2;
}

template<>
int func_sorted<float, int>()
{
    return 3;
}


template<typename Lhs, typename Rhs>
int func()
{
    typedef typename Sort<
        Lhs,
        Rhs,
        ((int)Order<Lhs>::Value > (int)Order<Rhs>::Value)>::First First;
    typedef typename Sort<
        Lhs,
        Rhs,
        ((int)Order<Lhs>::Value > (int)Order<Rhs>::Value)>::Second Second;
    return func_sorted<First, Second>();
}

所以关注代码

int main()
{
    std::cout << func<int, float>() << std::endl;
    std::cout << func<float, int>() << std::endl;
}

将打印:

  

2
2