简体   繁体   English

C ++ 17如果constexpr()与元组一起使用

[英]C++17 if constexpr() usage with tuple

I am using VS2017 with C++17 support enabled. 我正在使用VS2017并启用了C ++ 17支持。

I want to try to make a "Transformer" class that when provided with a certain type which is supported it will transform the type, otherwise it will return the variable as is. 我想尝试创建一个“Transformer”类,当提供支持的某种类型时,它将转换类型,否则它将按原样返回变量。 The goal is to pass all variable types to the transformer, and "hide" which ones its transforming. 目标是将所有变量类型传递给变换器,并“隐藏”其变换的变量。 This way callers can try to transform everything and don't need to worry about whether or not transforming is necessary, the transformer will know. 这样调用者可以尝试转换所有内容,而不必担心转换是否必要,变换器会知道。

A more complete example (edited from original): 一个更完整的例子(从原文编辑):

    class MyPoint
{
public:
    int x = 0;
};

class NotMyPoint
{
public:
    int x = 50;
};

template <typename T>
class ITransform
{
    public:

    virtual ~ITransform() {};

    virtual T InTransform(const T &in) const = 0;

    virtual T OutTransform(const T &out) const = 0;

    //Check if the argument type is the same as this class type
    template <typename X>
    constexpr bool CanTransform() const
    {
        return std::is_same<X, T>::value;
    }
};

class MyTransformer :
    public ITransform<MyPoint>
{
public:
    MyTransformer() = default;

    virtual MyPoint InTransform(const MyPoint &in) const override
    {
        auto newPt = in;
        newPt.x += 100;
        return newPt;
    }

    virtual MyPoint OutTransform(const MyPoint &in) const override
    {
        auto newPt = in;
        newPt.x -= 100;
        return newPt;
    }
};

template <class... TRANSFORMERS>
struct VariadicTransformer
{
    constexpr VariadicTransformer() = default;

    /** \brief parse using validateParse but catch throw */
    template <typename T>
    inline T Transform(const T& in)
    {
        return TransformImpl<sizeof...(TRANSFORMERS)-1, T>(in);
    }

private:
    /// last attempt to find matching transformer at I==0, if it fails return the original value
    template<std::size_t I = 0, typename T>
    inline typename std::enable_if<I == 0, T>::type TransformImpl(const T &in) const
    {
        if (std::get<I>(transformers).CanTransform<T>())
            return std::get<I>(transformers).InTransform(in);
        else
            return in;
    }

    /// attempt to find transformer for this type
    template<std::size_t I = 0, typename T>
    inline typename std::enable_if < I != 0, T>::type TransformImpl(const T &in) const
    {
        if (std::get<I>(transformers).CanTransform<T>())
            return std::get<I>(transformers).InTransform(in);
        else
            return TransformImpl<I - 1, T>(in);
    }

    std::tuple<const TRANSFORMERS...> transformers;
};


//Example usage

VariadicTransformer<MyTransformer, MyTransformer> varTrans;
MyPoint myPoint;
NotMyPoint notMyPoint;

std::cout << myPoint.x << std::endl;
myPoint = varTrans.Transform(myPoint);
std::cout << myPoint.x << std::endl;

std::cout << notMyPoint.x << std::endl;
notMyPoint = varTrans.Transform<NotMyPoint>(notMyPoint);
std::cout << notMyPoint.x << std::endl;

return 0;

My issue comes in with this line: 我的问题来自于这一行:

if constexpr(std::get<I>(transformers).CanTransform<T>())

This will not compile and provides the following error: 这将无法编译并提供以下错误:

error C2131: expression did not evaluate to a constant 错误C2131:表达式未计算为常量

note: failure was caused by a read of a variable outside its lifetime 注意:失败是由在其生命周期之外读取变量引起的

note: see usage of 'this' 注意:看看'this'的用法

The CanTransform function should be a constexpr, std::get<#>(std::tuple) should be a constexpr, so I am unsure what its complaint about this line is. CanTransform函数应该是constexpr,std :: get <#>(std :: tuple)应该是constexpr,所以我不确定它对此行的抱怨是什么。

Also the if constexpr is required to avoid trying to call any transformers which are not eligible to transform the current type, I want this case to fall through and return the original. 此外,if constexpr需要避免尝试调用任何不适合转换当前类型的变换器,我希望这种情况可以通过并返回原始。

Any advice on what is causing this error or another design I could try? 有关导致此错误的原因或我可以尝试的其他设计的任何建议吗?

A method call on an object will only be constexpr if the calling object is also constexpr . 在对象上的方法调用将只constexpr如果调用对象也是 constexpr If the calling object is not constexpr , then the method will still be evaluated at runtime instead of compiletime, and thus be ineligible for any compiletime evaluation. 如果调用对象不是constexpr ,那么该方法仍将在运行时而不是编译时进行评估,因此不适用于任何编译时评估。

struct A {
    int val;
    constexpr A() : A(16) {}
    constexpr A(int val) : val(val) {}
    constexpr bool foo() const {return val > 15;}
};

int main() {
    A a;
    if constexpr(a.foo()) {
        std::cout << "No point thinking about it; this won't compile!" << std::endl;
    } else {
        std::cout << "Again, the previous line doesn't compile." << std::endl;
    }

    constexpr A b;
    if constexpr(b.foo()) {
        std::cout << "This, however, will compile, and this message will be displayed!" << std::endl;
    }

    constexpr A c(13);
    if constexpr(c.foo()) {
        std::cout << "This will not be displayed because the function will evaluate to false, but it will compile!" << std::endl;
    }
}

You need to make sure that TransformImpl can be made constexpr , and then ensure that the instance of A which calls TransformImpl is also constexpr . 您需要确保TransformImpl可以成为constexpr ,然后确保调用TransformImplA实例也是constexpr

So I can't explain why the previous option wouldn't work. 所以我无法解释为什么以前的选项不起作用。 However, but by making the CanTransform function into its own standalone function the error went away. 但是,通过将CanTransform函数转换为自己的独立函数,错误就消失了。

This finally works: 这最终有效:

template<typename X, typename T>
constexpr inline bool CanTransform()
{
    return std::is_base_of<ITransform<T>, X>::value;
}

template <class... TRANSFORMERS>
struct VariadicTransformer
{
    constexpr VariadicTransformer() = default;

    template <typename T>
    constexpr inline T Transform(const T& in) const
    {
        return TransformImpl<sizeof...(TRANSFORMERS)-1, T>(in);
    }

private:
        // last attempt to find matching transformer at I==0, if it fails return the original value
    template<std::size_t I = 0, typename T>
    constexpr inline typename std::enable_if<I == 0, T>::type TransformImpl(const T &in) const
    {
        if constexpr(CanTransform < std::tuple_element < I, std::tuple<TRANSFORMERS...>>::type, T >())
            return std::get<I>(transformers).InTransform(in);
        else
            return in;
    }

       // attempt to find transformer for this type
    template<std::size_t I = 0, typename T>
    constexpr inline typename std::enable_if < I != 0, T>::type TransformImpl(const T &in) const
    {
        if constexpr(CanTransform < std::tuple_element < I, std::tuple<TRANSFORMERS...>>::type, T >())
            return std::get<I>(transformers).InTransform(in);
        else
            return TransformImpl<I - 1, T>(in);
    }

    std::tuple<TRANSFORMERS...> transformers;
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM