【问题标题】:C++17 if constexpr() usage with tupleC++17 if constexpr() 与元组一起使用
【发布时间】:2017-12-11 19:29:43
【问题描述】:

我正在使用启用了 C++17 支持的 VS2017。

我想尝试创建一个“Transformer”类,当提供某种受支持的类型时,它将转换类型,否则它将按原样返回变量。目标是将所有变量类型传递给转换器,并“隐藏”它的转换类型。这样调用者可以尝试转换所有内容,而不必担心是否需要转换,转换器会知道。

一个更完整的例子(根据原文编辑):

    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;

我的问题在于这一行:

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

这将无法编译并提供以下错误:

错误 C2131:表达式未计算为常量

注意:失败是由在其生命周期之外读取变量引起的

注意:查看“this”的用法

CanTransform 函数应该是 constexpr,std::get(std::tuple) 应该是 constexpr,所以我不确定它对这一行的抱怨是什么。

还需要 if constexpr 以避免尝试调用任何不适合转换当前类型的转换器,我希望这种情况能够通过并返回原始类型。

关于导致此错误的原因或我可以尝试其他设计的任何建议?

【问题讨论】:

  • std::get&lt;I&gt;(this-&gt;transformers).CanTransform&lt;T&gt;()std::get&lt;I&gt;(this-&gt;transformers).template CanTransform&lt;T&gt;()
  • return constexpr(...); 不是有效的 C++。
  • @chris 是 Visual C++ 扩展还是错误?我无法快速找到有关此类构造的任何信息。
  • @Constructor,除了提案中的constexpr 运算符外,我没有遇到过任何类似的情况。
  • 只需将CanTransform 设为静态即可。它不访问任何成员,因此没有理由需要是非静态的。如果它是静态的,则不再需要 *this 对象 constexpr

标签: c++ templates c++14 variadic-templates c++17


【解决方案1】:

如果调用对象 constexpr,对象上的方法调用只会是constexpr。如果调用对象不是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;
    }
}

你需要确保TransformImpl可以变成constexpr,然后确保调用TransformImplA的实例也是constexpr

【讨论】:

  • a.foo() 确实为我编译,但 b.foo() 没有,因为 foo 需要声明为 foo() const,因为 b 是一个 const 并且不允许调用 foo() .
  • 哦,非constexpr 类成员不能在constexpr if 条件下发生。
  • @Xirema 我经历了一切,并尽我所能将所有内容都设为 constexpr,编译器仍在抱怨访问“this”使其成为非 const。我确实找到的一种方法是将元组设为“静态 constexpr”,但这并不理想。建议?
【解决方案2】:

所以我无法解释为什么前一个选项不起作用。然而,但是通过将 CanTransform 函数变成它自己的独立函数,错误就消失了。

这终于奏效了:

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;
};

【讨论】:

  • 它起作用了,因为你创建了函数constexpr
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-23
  • 2022-01-10
  • 2021-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多