C ++模板中条件代码实例化的最简洁方法

时间:2016-01-04 10:51:17

标签: c++ templates

我试图让以下C ++代码运行:

#include <cmath>

template<typename T, bool> class ConditionalData {
};

template <typename T> class ConditionalData<T, false> {
};

template <typename T> class ConditionalData<T, true> {
private:
    T data;
public:
    void setData(T _data) { data = _data; }
};


template<bool hasdata> class A {
public:
    A() {
        ConditionalData<double,hasdata> data;
        if (hasdata) {
            data.setData(sin(cos(123.4)));
        }
    }
};


int main(int argNum, const char**argData) {
    A<false> test1;
    A<true> test2;
    return 0;
}

本质上,我想实现一个模板化的类A,其中根据模板参数执行某些操作。这些操作需要局部变量,我只想在需要时分配它们。我在这里遇到的问题是

的主体
if (hasdata) {
    data.setData(3);
}

条件也是为hasdata = false实例化的,它不能编译(使用g ++ 5.2)。任何想法如何以最干净的方式完成这项工作而不将A :: A()的主体分成几部分?

上面的源代码是一个最小的非工作示例。 A :: A()的真正实现相对较长,部分依赖于&#34; hasdata&#34;在代码上均匀分布。此外,&#34; typename T&#34;将使用类A的是一个相对复杂的类,具有重量级构造函数/析构函数,因此我希望仅在hasdata = true时分配T的实例。最后,在data.setData(...)调用中,&#34; ...&#34;中可能存在复杂的计算,只有在需要时才会执行。

6 个答案:

答案 0 :(得分:12)

如果您能负担,则可以将条件分支表示为通用lambdas。好处是它们捕获周围的变量,解决方案不需要额外的成员函数。

template <bool> struct tag {};

template <typename T, typename F>
auto static_if(tag<true>, T t, F f) { return t; }

template <typename T, typename F>
auto static_if(tag<false>, T t, F f) { return f; }

template <bool B, typename T, typename F>
auto static_if(T t, F f) { return static_if(tag<B>{}, t, f); }

template <bool B, typename T>
auto static_if(T t) { return static_if(tag<B>{}, t, [](auto&&...){}); }

// ...

ConditionalData<int, hasdata> data;        
static_if<hasdata>([&](auto& d)
{
    d.setData(3);
})(data);

DEMO

中你可以说:

if constexpr (hasdata)
{
    data.setData(3);
}

DEMO 2

答案 1 :(得分:7)

这是一种常见的模式,因此实际上有一篇文章要将constexpr_if添加到C ++中。如果它适用于未来版本,它将允许您保持代码非常原样。

template<bool hasdata> class A {
public:
    A() {
        ConditionalData<int,hasdata> data;
        constexpr_if (hasdata) {
        //^^^^^^^^^^ instead of plain if
            data.setData(3);
        }
    }
};

目前,你需要处理其他一个答案。

编辑:这已添加到C ++ 17并调用if constexpr

答案 2 :(得分:5)

首先,您不需要3个class ConditionalData版本,因为bool可以是truefalse。所以让我简化如下:

template<typename T, bool = false> class ConditionalData {
};                 //^^^^^^^^^^^^

template <typename T> class ConditionalData<T, true> {
private:
    T data;
public:
    void setData(T _data) { data = _data; }
};

其次,要回答您的问题:无论哪个成员属于false类别,只需将其超出class机构,如下所示:

template<bool hasdata> class A { 
public:
    A() {
        ConditionalData<int,hasdata> data;
        if (hasdata) {
            data.setData(3);
        }
    }   
};

template<> A<false>::A() {}  // Does nothing for `false` condition

答案 3 :(得分:4)

您可以为两个分支定义setData,为false条件清空{

}

template<typename T, bool> class ConditionalData {
};

template <typename T> class ConditionalData<T, false> {
    void setData(T _data) {}
};

template <typename T> class ConditionalData<T, true> {
private:
    T data;
public:
    void setData(T _data) { data = _data; }
};

template<bool hasdata> class A {
public:
    A() {
        ConditionalData<int,hasdata> data;
        data.setData(3);
    }
};

答案 4 :(得分:3)

如果您不能(/不想)更改ConditionalData,您可以改为创建2种方法:

template<typename T>
void SetData(ConditionalData<T, false>& , const T& ) {/* Nothing */}

template<typename T>
void SetData(ConditionalData<T, true>& c, const T& value) { c.setData(value); }

然后

A() {
    ConditionalData<int, hasdata> data;
    SetData(data, 3);
}

对于更复杂的案例

template<typename T>
void A_impl_part1(ConditionalData<T, false>&) {/* Nothing */}

template<typename T>
void A_impl_part1(ConditionalData<T, true>& c) { c.setData(3); }

然后

A() {
    ConditionalData<int, hasdata> data;
    A_impl_part1(data);
    // common part
    // A_impl_part2(data); // and so on
}

答案 5 :(得分:2)

您可以使用预处理器“生成”类的每个变体作为模板特化。

首先,'模板'标题我们将从:

生成特化
ATemplate.h
//no include guards to allow multiple inclusion
template<>
class A<A_HAS_DATA>
{
public:
    A()
    {
#if A_HAS_DATA
        double data;
        if (hasdata) {
            data = sin(cos(123.4));
        }
#endif
    }
}

然后我们实际生成每个特化以获得在代码中使用的普通标头:

A.h
#pragma once

template<bool hasdata>
class A;

//Generate specialization for hasdata = true
#define A_HAS_DATA 1
#include "ATemplate.h"
#undef A_HAS_DATA
//(undef avoids redefinition warning)
//Generate specialization for hasdata = false
#define A_HAS_DATA 0
#include "ATemplate.h"
#undef A_HAS_DATA

本质上,我们不是手动为每个可能的情况编写每个特化(假设您可能有多个这样的设置来包含/排除东西),我们使用预处理器通过多次包含标题来生成每个变体,每次都有不同的预处理器的值定义以获得不同的结果。

在工作时喜欢使用普通的模板方法,但如果手动代码重复的数量(定义所有可能的变体)增长得太高,这种方法可以工作(因为你可以指定所有'内联'类似于静态的如果我们有一个会这样做)