2017-03-14 3 views
4

私はパラメータとしてstd::functionを取る関数を持っています。しかし、渡される関数が渡されたパラメータを変更することを許可されないようにしたい。lambdaをstd :: functionに変換する際にconst-correctnessを適用する

はここで機能の簡易版です(Tは、および通常は、参照できることに注意してください):

template <class T> 
void Bar(std::function<void(std::add_const_t<T>)> func) 
{ 
    // ... 
} 

悪い用法:

私はその使用を許可しないようにしたい
Bar<int&>([](int&) { /* Do nasty stuff! */ }); /* A-OK! */ 

、しかし、このコードは完璧にコンパイルされますが、私はそうすべきではないと感じています。 (ASすべきでない)

void Bar(std::function<void(const int&)> func) 
{ 
    // ... 
} 

次に、この用法はコンパイルされないでしょう:

Bar([](int&) { /* Do nasty stuff! */ }); /* Error C2664 */ 

どのように私はすなわち、テンプレートパラメータを取り除く場合

興味深いのは、あります私はこれを強制し、テンプレートパラメータを保持することはできますか?

+0

注 'のstd :: add_const_t を' '' int型&です。 – aschepler

+0

@aschepler私はドキュメントでもそれを見ました。だから私はハッキーなバージョンをやってみました: 'const T'、私は' const int& 'に推論すると思っていました。しかし、いいえ。 'std :: add_const_t 'を 'const T'に置き換えても、それはまだうまくコンパイルされます。 – Zeenobit

+0

@Zeenobit:テンプレートはマクロではありません。これは 'typedef int & T; T const ref;より似ています; - ' ref'は 'int&'になります。また、ポインタと比較する: 'typedef int * T; T const ptr'は 'int const *'ではなく 'int * const'を取得します。 – MSalters

答えて

5

std::add_const_t<int &>int &です。constintに追加していないことに注意してください。代わりに、への参照にconstを追加し、const intへの参照ではなく、int(つまりint &)のconst参照を取得します。それが動作することを

Bar<int&>([](const int &) {}); 

注:

それができる回避するための簡単な方法:あなたはそれを回していない限り

#include<functional> 
#include<type_traits> 

template<typename T> 
struct to_const_ { using type = std::add_const_t<T>; }; 

template<typename T> 
struct to_const_<T &> { using type = std::add_const_t<T> &; }; 

template<typename T> 
using to_const_t = typename to_const_<T>::type; 

template <class T> 
void Bar(std::function<void(to_const_t<T>)> func) 
{} 

int main() { 
    Bar<int&>([](int&) {}); 
} 

上記のコードは、(要求されたとして)コンパイルされません。それはそのままの左辺値参照でのみ正しく表示されますが、右辺値参照とポインタのサポートを追加することは簡単です。


それは最小限、(おそらく)実施例は以下:

#include<functional> 
#include<type_traits> 

template<typename T> 
struct to_const_ { using type = std::add_const_t<T>; }; 

template<typename T> 
struct to_const_<T &> { using type = std::add_const_t<T> &; }; 

template<typename T> 
struct to_const_<T &&> { using type = std::add_const_t<T> &&; }; 

template<typename T> 
struct to_const_<T * const> { using type = std::conditional_t<std::is_pointer<T>::value, typename to_const_<T>::type * const, std::add_const_t<typename to_const_<T>::type> * const>; }; 

template<typename T> 
struct to_const_<T *> { using type = std::conditional_t<std::is_pointer<T>::value, typename to_const_<T>::type *, std::add_const_t<typename to_const_<T>::type> *>; }; 

template<typename T> 
using to_const_t = typename to_const_<T>::type; 

template <class T> 
void Bar(std::function<void(to_const_t<T>)> func) 
{} 

int main() { 
    Bar<int **>([](const int **) {}); 
} 
+0

これは完璧です。ありがとう! – Zeenobit

0

static_assertを使用すると、テンプレートの助けを借りてコンパイルに失敗することがあります。値渡し

  1. (下記参照):Tがあるときに成功する。これはのみに設定されている

    template <class T> 
    void Bar(std::function<void(T)>) 
    { 
        static_assert(is_const_param<T>::value, "..."); 
    } 
    

    //Base case - T is non-const, possibly pointer or reference 
    template <typename T> struct is_const_param { static constexpr bool value = !std::is_lvalue_reference<T>::value && !std::is_rvalue_reference<T>::value && !std::is_pointer<T>::value; }; 
    
    //T is const, but possibly pointer to non-const 
    template <typename T> struct is_const_param<const T> { static constexpr bool value = !std::is_pointer<T>::value; }; 
    
    //Remove reference, try again 
    template <typename T> struct is_const_param<const T&> : is_const_param<const T> { }; 
    
    //Remove reference, try again 
    template <typename T> struct is_const_param<const T&&> : is_const_param<const T> { }; 
    
    //Remove pointer, try again 
    template <typename T> struct is_const_param<const T*> : is_const_param<const T> { }; 
    
    //Remove pointer, try again 
    template <typename T> struct is_const_param<const T* const> : is_const_param<const T> { }; 
    

    static_assertはただのようになります。

  2. 定数参照渡し。
  3. ポインタを定数に渡します。

テストケースhereは、成功または失敗の例を見たい場合に表示されます。


Bar<int>のように通常値渡しを許可するように設定されています。 Bar<const int>のみを許可するように変更する場合は、is_const_param<const T*>の特殊化を削除し、未特殊化テンプレートのvalueの初期化をfalseに変更してください。それはhereです。

+0

'T **'はどうですか? – skypjack

関連する問題