22

次のコードを検討してください。

#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(非定数)の場合、問題はありません。ただし、y(constデータ型)は、コンパイル時にif / elseの条件がわかっている場合でも、エラーを引き起こします。

コンパイル時にfalse条件をコンパイルしない可能性はありますか?

4

6 に答える 6

20

C ++ 17if 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

参照:「ifconstexpr()」と「if()」の違い

これは、C ++20の「文字列リテラルテンプレート引数」と一緒に使用すると非常に便利です。文字列リテラルをパラメータとしてC++テンプレートクラスに渡します。

Ubuntu 16.04、GCC8.1.0でテスト済み

于 2019-02-12T09:58:15.000 に答える
14

最も簡単な修正は、部分的なテンプレートの特殊化です。

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;
};
于 2012-08-28T14:02:54.907 に答える
5

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
};
于 2012-08-28T14:04:19.667 に答える
3

クラステンプレートは、指定されたタイプ用にコンパイルされます。制御フローが割り当てに到達しない場合でも、その割り当てもコンパイルされます。メンバーは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;
}
于 2012-08-28T13:57:41.970 に答える
0

elseブランチがコンパイルされていない場合、関数の意味はまったく異なります。コードの一部をコンパイルできないだけではいけません。実行したくない場合は、記述しないでください。関数が呼び出されるたびに個別にコンパイルされるわけではありません。

const型システムの要点は、変数への割り当てなどを誤って実行しようとしないようにすることです。その変数に割り当てられない、まったく新しい(またはオーバーロードされた)関数を作成する必要があります。

于 2012-08-28T13:54:52.447 に答える
0

これを試して:

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)
    {

    }
};
于 2012-08-28T14:11:32.003 に答える