if / else at compile time in C++?



考虑以下代码:

#include <iostream>
#include <type_traits>
template<typename T> class MyClass
{
    public:
        MyClass() : myVar{0} {;}
        void testIf() {
            if (isconst) {
                myVar;
            } else {
                myVar = 3;
            }
        }
        void testTernary() {
            (isconst) ? (myVar) : (myVar = 3);
        }
    protected:
        static const bool isconst = std::is_const<T>::value;
        T myVar;
};
int main()
{
    MyClass<double> x;
    MyClass<const double> y;
    x.testIf();
    x.testTernary();
    y.testIf(); // <- ERROR
    y.testTernary(); // <- ERROR
    return 0;
}

对于x(非常数)没有问题。但是,即使在编译时if/else中的条件已知,y(const数据类型)也会导致错误。

是否有可能在编译时不编译false条件?

C++17 if constexpr

哦,是的,它已经到了:

main.cpp

#include <cassert>
#include <type_traits>
template<typename T>
class MyClass {
    public:
        MyClass() : myVar{0} {}
        void modifyIfNotConst() {
            if constexpr(!isconst) {
                myVar = 1;
            }
        }
        T myVar;
    protected:
        static constexpr bool isconst = std::is_const<T>::value;
};
int main() {
    MyClass<double> x;
    MyClass<const double> y;
    x.modifyIfNotConst();
    y.modifyIfNotConst();
    assert(x.myVar == 1);
    assert(y.myVar == 0);
    return 0;
}

GitHub上游。

编译并运行:

g++-8 -std=c++17 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

另见";如果constexpr()"Vs";如果()"

这与C++20"字符串文本模板参数"一起将非常酷:将字符串文本作为参数传递给C++模板类

在Ubuntu 16.04,GCC 8.1.0中测试。

最简单的修复方法是部分模板专用化:

template<typename T> class MyClassBase
{
    public:
        MyClassBase() : myVar{0} {;}
    protected:
        T myVar;
};
template<typename T> class MyClass: MyClassBase<T>
{
    public:
        void testIf() { myVar = 3; }
};
template<typename T> class MyClass<const T>: MyClassBase<const T>
{
    public:
        void testIf() { myVar; }
};

另一种选择是授权:

template<typename T> class MyClass
{
    public:
        MyClass() : myVar{0} {;}
        void testIf() { testIf_impl(std::integral_constant<bool, isconst>()); }
    protected:
        static const bool isconst = std::is_const<T>::value;
        T myVar;
    private:
        void testIf_impl(std::true_type) { myvar; }
        void testIf_impl(std::false_type) { myVar = 3; }
};

SFINAE是另一种选择,但通常不适合这种情况:

template<typename T> class MyClass
{
    public:
        MyClass() : myVar{0} {;}
        template
        <typename U = void>
        typename std::enable_if<std::is_const<T>::value, U>::type testIf() { myvar; }
        template
        <typename U = void>
        typename std::enable_if<!std::is_const<T>::value, U>::type testIf() { myvar = 3; }
    protected:
        static const bool isconst = std::is_const<T>::value;
        T myVar;
};

您可以将类专门化为const类型

template<typename T>
class MyClass 
{
   // Whatever you need to do
};
template<typename T>
class MyClass<const T> 
{
   // Whatever you need to do for const types
};

为给定类型编译类模板。即使控制流没有到达赋值,也会编译该赋值。由于成员是const,因此编译将失败。

你可以使用某种形式的SFINAE来跳过这项任务,但它不会像现在这样起作用。

这很有效(为了简单起见,我删除了testTernary成员函数):

#include <iostream>
#include <type_traits>
template<typename T> class MyClass
{
    public:
        MyClass() : myVar{0} {;}
        template<class U = T>
        typename std::enable_if<std::is_const<U>::value>::type testIf() {
            myVar;
        }
        template<class U = T>
        typename std::enable_if<!std::is_const<U>::value>::type testIf() {
            myVar = 3;
        }
    protected:
        static const bool isconst = std::is_const<T>::value;
        T myVar;
};
int main()
{
    MyClass<double> x;
    MyClass<const double> y;
    x.testIf();
    y.testIf();
    return 0;
}

如果没有编译else分支,那么您的函数将具有完全不同的含义。你不能只是不编译部分代码。如果你不想让它执行,就不要写它。这不像每次调用函数都要单独编译。

类型系统的全部目的是避免意外地尝试执行诸如分配给const变量之类的操作。您必须编写一个全新的(或重载的)函数,该函数不分配给该变量。

试试这个:

template<typename T> 
class MyClass
{
    T myVar;
public:
    MyClass() : myVar(0) {}
    void testIf()
    {
        assign(myVar, 3);
    }
 private:
    template<typename V>
    void assign(V& destination, int value)
    {
        destination = value;
    }
    template<typename V>
    void assign(const V& destination, int value)
    {
    }
};

最新更新