constexpr 数据成员和单一定义规则

问题描述 投票:0回答:1

我想定义一个特殊的实例,用于分支到优化的代码路径,如下所示:

//------------------ header ----------------------
template <typename T> 
class Thingy
{
    T value_;

public:
    inline static const Thingy Special; //ODR-usable
    constexpr Thingy() : value_(42) {}
    constexpr Thingy( T v) : value_(v){}
    
    constexpr Thingy CombineWith( const Thingy& other) const
    {
        return Thingy( value_ + other.value_);
    }
};

using RealThingy = Thingy<float>;

//------------------ library ---------------------
#include <iostream>
void UseThingy( const RealThingy& t = RealThingy::Special)
{
    if( &t == &RealThingy::Special){
        std::cout << "do cheap default task\n";
    }else{
        //checking for 42 first is too expensive
        std::cout << "sigh, do expensive task\n";
    }
}

//------------------- client ---------------------
void main()
{
    UseThingy(); //should be cheap
    UseThingy( RealThingy::Special); //should be cheap
    UseThingy( RealThingy(42.)); //expensive, but that's okay
    UseThingy( RealThingy(21.)); //expensive, as it must
    
    constexpr RealThingy t1{21.}, t2{21.};
    constexpr RealThingy t3 = t1.CombineWith(t2);
    //constexpr RealThingy t4 = t1.CombineWith(RealThingy::Special);
}

注意

Special
充当单例,以便快速决策以分支到优化算法。如果客户端使用相当于
Special
的实例,一切仍然有效,只是速度较慢。

除了示例中的最后一行之外,一切都很好。我希望

Special
constexpr
,但如果我在
inline
的定义中将
constexpr
替换为
Special
,编译器 (clang C++17) 会抱怨“Constexpr 变量不能具有非文字类型” ”.

Special
的正确咒语是什么(不使用单独的 .cpp 文件来表示
Thingy
)?

顺便说一下,我需要使用C++17。

c++ constexpr static-members one-definition-rule
1个回答
0
投票

据我所知,问题实际上是你的

Special
类型不完整。

目前,我能找到的唯一解决方法是添加一个额外的命名层,如下所示:

template <typename T>
class Thingy
{
    const T value_;

public:
    constexpr Thingy() : value_(42) {}
    constexpr Thingy( T v) : value_(v){}

    constexpr Thingy CombineWith( const Thingy& other) const
    {
        return Thingy( value_ + other.value_);
    }

    struct Specials;
};

template <typename T> struct Thingy<T>::Specials
{
    static constexpr Thingy<T> Special{};
};

using RealThingy = Thingy<float>;

#include <iostream>
void UseThingy( const RealThingy& t = RealThingy::Specials::Special)
{
    if( &t == &RealThingy::Specials::Special){
        std::cout << "do cheap default task\n";
    }else{
        //checking for 42 first is too expensive
        std::cout << "sigh, do expensive task\n";
    }
}

int main()
{
    UseThingy(); //should be cheap
    UseThingy( RealThingy::Specials::Special); //should be cheap
    UseThingy( RealThingy(42.)); //expensive, but that's okay
    UseThingy( RealThingy(21.)); //expensive, as it must

    constexpr RealThingy t1{21.}, t2{21.};
    constexpr RealThingy t3 = t1.CombineWith(t2);
    constexpr RealThingy t4 = t1.CombineWith(RealThingy::Specials::Special);

    return 0;
}

当编译器到达

Specials
时,
Thingy
模板已经完成,因此
Thingy<T>
现在是一个完整的类型,可以用作文字类型。

© www.soinside.com 2019 - 2024. All rights reserved.