【问题标题】:Best way to handle multiple conditions for conditional compilation处理条件编译的多个条件的最佳方法
【发布时间】:2017-10-31 20:11:00
【问题描述】:

我有一个模板类,它有一个方法,模板参数指示该方法的输入和输出,如下所示:

template <typename In, typename Out>
class Foo
{
    Out fn(const In& in)
    {
        Out out;
        return out;
    }
}

所以我尝试了这个,但是当尝试将void 用于InOut 时会出现错误(可能很明显)。所以我尝试添加多个方法,这些方法是这个主题的变体,希望它们的替换能够启用相关功能并禁用无效功能:

template <std::enable_if_t<std::is_void<InputType>::value>* = nullptr>
OutputType fn()
{
    OutputType out;
    return out;
}

template <std::enable_if<(!std::is_void<OutputType>::value) && (!std::is_void<InputType>::value)>* = nullptr>
OutputType fn(InputType& t)
{
    OutputType out;
    return out;
}

template <std::enable_if<std::is_void<OutputType>::value>* = nullptr>
void fn(InputType& t)
{}

这让我回到“无效引用无效”领域,或者签名冲突。

我应该如何优雅地处理这些情况,以便从模板中只创建以下签名之一:

/*In == void && Out != void*/
Out fn(/* no input here to keep compiler happy*/) { return Out; }

/*In != void && Out != void, standard case*/
Out fn(const In& in) { return Out; }

/*In != void && Out == void*/
void fn(const In& in) { /* No returns here to keep compiler happy*/; }

【问题讨论】:

标签: c++ templates c++14 sfinae


【解决方案1】:

InOut 中的任何一个为空时,您可以使用partial specialisation 提供Foo 的实现,当两者都为空时,explicit specialisation 提供。

语法如下(注意Foo 后面的尖括号表示这是主Foo 类模板的特化

template<typename Out>
struct Foo<void, Out>  // specialisation for only In = void
{ ... };

template<typename In>
struct Foo<In, void>   // specialisation for only Out = void
{ ... };

template<>
struct Foo<void, void> // specialisation for both In and Out = void
{ ... };

这是一个例子:

#include <iostream>

// primary class template
template <typename In, typename Out>
struct Foo {
    Out fn(const In& in) { return Out{}; }
};

// partial specialisation for when In=void
template<typename Out>
struct Foo<void, Out> {
    Out fn() { return Out{}; }
};

// partial specialisation for when Out=void
template<typename In>
struct Foo<In, void> {
    void fn(const In& in) { }
};

// explicit specialisation for when both In=void and Out=void
template<>
struct Foo<void, void> {
    void fn() { }
};

int main() {
    Foo<int, double> f;
    f.fn(5);

    Foo<void, void> g;
    g.fn();

    Foo<void, int> h;
    h.fn();

    Foo<int, void> i;
    i.fn(5);

    return 0;
}

【讨论】:

    【解决方案2】:

    返回值和输入值是不同的问题,可以独立解决。

    首先,我建议你切换模板类型的顺序:先Out,然后再In

    这是因为如果您在输入类型的可变参数列表中转换 In

    template <typename Out, typename ... Ins>
    struct Foo { /* ... */ };
    

    你自动解决void输入类型(你根本不表达它)。

    不是一个很好的解决方案,因为输入值可能难以使用;只是为了向您展示如何创建适用于所有情况的结构/方法。

    对于返回void类型,你可以简单地做

    return (Out)someVal;
    

    其中someVal 属于Out 类型,而Out 不是void,如果Outvoid,则属于另一种类型(例如:int)。

    所以如果你定义一个类型特征deVoid如下

    template <typename T>
    struct deVoid
     { using type = T; };
    
    template <>
    struct deVoid<void>
     { using type = int; }; // a fake not-void type
    
    template <typename T>
    using deVoid_t = typename deVoid<T>::type;
    

    你可以定义out变量如下

      deVoid_t<Out> out {};
    

    return 这样

      return (Out)out;
    

    Outvoid 时也有效。

    所以你可以写Foo如下

    template <typename Out, typename ... Ins>
    struct Foo
     {
       Out fn (Ins const & ... ins)
        {
          deVoid_t<Out> out {};
    
          return (Out)out;
        }
     };
    

    适用于零输入类型(例如 void 输入类型)和 void 返回类型。

    以下是一个完整的工作示例

    template <typename T>
    struct deVoid
     { using type = T; };
    
    template <>
    struct deVoid<void>
     { using type = int; }; // a fake not-void type
    
    template <typename T>
    using deVoid_t = typename deVoid<T>::type;
    
    template <typename Out, typename ... Ins>
    struct Foo
     {
       Out fn (Ins const & ... ins)
        {
          deVoid_t<Out> out {};
    
          return (Out)out;
        }
     };
    
    int main ()
     {
       Foo<int, int>         f; f.fn(42);
       Foo<void>             g; g.fn();
       Foo<int>              h; h.fn();
       Foo<void, int>        i; i.fn(42);
       Foo<void, int, long>  j; j.fn(42, 84L);
     }
    

    【讨论】:

      猜你喜欢
      • 2021-08-18
      • 1970-01-01
      • 1970-01-01
      • 2021-10-28
      • 1970-01-01
      • 2021-09-18
      • 2010-09-21
      • 2022-01-27
      • 1970-01-01
      相关资源
      最近更新 更多