9

次のコードは、cppreference.comから抜粋したものです。

#include <iostream>
#include <type_traits>

struct foo
{
    void m() { std::cout << "Non-cv\n"; }
    void m() const { std::cout << "Const\n"; }
};

template <class T>
void call_m()
{
    T().m();
}

int main()
{
    call_m<foo>();
    call_m<std::add_const<foo>::type>();
}

ただし、VC ++ 2012年11月CTPでコンパイルすると、出力は次のようになります。

非履歴書

非履歴書

予想ではなく:

非履歴書

定数

さらに、次の2つのステートメントの違いは何ですか。

call_m<const foo>();

call_m<std::add_const<foo>::type>();

4

2 に答える 2

10

これは MSVC のバグのようです。形式の式T()(標準に関する限り、明示的な型変換) を使用すると、指定された型の prvalue が得られます。

式はT()、配列ではない完全なオブジェクト型または (おそらく cv 修飾された)型Tsimple-type-specifierまたはtypename-specifiervoidであり、指定された型の prvalue を作成します。これは値で初期化されます。

const非クラスの prvalues は cv 修飾された型を持つことができないという規則により、無視されるのは非クラスの型の場合のみです。

クラスの prvalue は、cv 修飾された型を持つことができます。非クラスの prvalues は常に cv 修飾されていない型を持ちます。

T()したがって、ここで作成された一時オブジェクトは、メンバー関数constを呼び出す必要があります。const

いつ、どのような理由で を使用するかについては、提案std::add_constに含めた理由を確認できます。、、、、およびタイプの特性が提案から削除されたが、Boost のユーザーからの苦情を受けて元に戻されたと述べています。add_constadd_volatileadd_cvadd_pointeradd_reference

理論的根拠は、これらのテンプレートはすべて、ある型を別の型に変換するコンパイル時のファンクターとして使用されるということです [...]

与えられた例は次のとおりです。

// transforms 'tuple<T1,T2,..,Tn>'
// to 'tuple<T1 const&,T2 const&,..,Tn const&>'
template< typename Tuple >
struct tuple_of_refs
{
   // transform tuple element types
   typedef typename mpl::transform<
      typename Tuple::elements,
      add_reference< add_const<_1> > // here!
   >::type refs;
   typedef typename tuple_from_sequence<refs>::type type;
};

template< typename Tuple >
typename tuple_of_refs<Tuple>::type
tuple_ref(Tuple const& t)
{
    return typename tuple_of_refs<Tuple>::type(t);
}

mpl::transform関数ポインターに相当するコンパイル時のメタプログラミングを 2 番目のテンプレート引数として取ると考えることができます-add_reference<add_const<...>>は の各型に適用されTuple::elementsます。これは単に を使用して表現できませんでしconstた。

于 2013-02-28T13:09:00.890 に答える
-1

私が覚えていることから、次のようになると、関数宣言に3つのconst(3は目的の数)を記述できます。

戻り値の型の前、関数とそのパラメーターの後、およびパラメーター自体の上。

関数シグネチャの末尾にある const は、関数がメンバであるオブジェクトが const であると仮定する必要があることを意味します。実際には、メンバー関数がオブジェクト データを変更しないことをコンパイラに確認させることを意味します。これは、コンパイラがメンバー データを直接変更しないこと、およびオブジェクトが変更されないことを保証しない関数を呼び出さないことを確認するようコンパイラに要求することを意味します。

戻り値の型が、関数が返すものを const にする必要があることを意味する前に。

const パラメータは、パラメータを変更できないことを意味します。

したがって、ここでの違いは、最初の呼び出しは const ではないため、「non-cv」に移動し、2 番目の呼び出しは const であるため、「const」に移動することです。

VC++ が両方の回で同じ関数に移動する理由について私が考えるのは、call_m が明示的に T().m() を呼び出し、const 関数に移動すべきではないと考えていることです。

于 2013-02-28T13:19:16.233 に答える