2017-12-11 9 views
1

VS2017でC++ 17サポートを有効にしています。C++ 17 texleでconstexpr()を使用する場合

サポートされている特定の型が提供されると型を変換する "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:式が定数に評価されませんでした

注:failuべきconstexprの、のstd :: <#>(STD ::タプル)を取得する必要があります「この」

CanTransform機能の参照使用:その寿命外変数の読み取りによって

ノートを引き起こした再私はこの行についての苦情が何であるか不明です。

また、現在のタイプを変換することができないトランスを呼び出そうとするのを避けるためにconstexprが必要な場合は、このケースをフォールバックして元に戻します。

このエラーの原因とは何かに関するアドバイスや、別のデザインを試すことができますか?

+0

'のstd ::取得(this->変圧器).CanTransform ()' '→のstd ::を(this->変圧器)を取得.TEMPLATE CanTransform ( ) ' – Constructor

+3

' return constexpr(...); 'は有効ではありませんC++。 – chris

+0

@chrisこれはVisual C++の拡張機能かバグですか?私はそのような建設に関する情報をすばやく見つけることはできません。 – Constructor

答えて

2

呼び出しオブジェクトがでも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; 
    } 
} 

あなたはTransformImplconstexpr作られ、その後、TransformImplを呼び出すAのインスタンスもconstexprであることを確認することができていることを確認する必要があります。

+0

bはconstなので、foo()の呼び出しは 'foo()'と宣言する必要があるので、 'a.foo()'は私のためにコンパイルしますが、 'b.foo()その後は許可されません。 – qdbp

+0

ああ、単純に 'constexpr if'条件で非'constexpr'クラスメンバを実行することはできません。 – Constructor

+0

@qdbpコードを修正しました。 – Xirema

0

私は前のオプションが機能しない理由を説明できません。しかし、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; 
}; 
関連する問題