2016-08-20 23 views
5

std::pairとその種類の値の1つの値をとる関数テンプレートがあります。私はstd::mapからのエントリをペア引数として使ってこの関数を呼びたいと思います。テンプレート引数の控除でconst/non-constの競合を解決する方法

#include <map> 
#include <utility> 

template <typename T1, typename T2> 
void do_stuff(std::pair<T1, T2> const &pair, T1 const &val) { 
    // Imagine that this does something important... 
} 

int main() { 
    std::map<int, float> foo { { 0, 0.0 } }; 

    do_stuff(*foo.begin(), 0); 
} 

これは、マップのエントリのタイプはstd::pair<const int, float>ので、T1のための型推論は、競合のタイプを持っているので、コンパイルに失敗します。const intpair引数、およびint経由val引数を経由して。

test.cc: In function ‘int main()’: 
test.cc:12:27: error: no matching function for call to ‘do_stuff(std::pair<const int, float>&, int)’ 
    do_stuff(*foo.begin(), 0); 
         ^
test.cc:5:6: note: candidate: template<class T1, class T2> void do_stuff(const std::pair<_T1, _T2>&, const T1&) 
void do_stuff(std::pair<T1, T2> const &pair, T1 const &val) { 
     ^~~~~~~~ 
test.cc:5:6: note: template argument deduction/substitution failed: 
test.cc:12:27: note: deduced conflicting types for parameter ‘const T1’ (‘const int’ and ‘int’) 
    do_stuff(*foo.begin(), 0); 
         ^

この競合を解決するにはどうすればよいですか?理想的には、T1intと推測したいと思いますが、実装がより簡単な場合はconst intにしても問題ありません。場合

void do_stuff(std::pair<T1, T2> const &pair, typename std::remove_const<T1>::type const &val) { 

が、私はより適切であるそれらのどれかわからない、または:

私はvalパラメータの種類のいずれかstd::remove_constまたはstd::decayを使用して、エラーを解決できることを発見しました他にも良い方法があります。

+0

わかりません...それに応じてdo_stuff()を定義してください。 'テンプレート void do_stuff(std :: pair const&pair、T1 const&val)'? – max66

+0

@ max66、私も最初の型*が* constではないペアでも動作したいです。 (パラメータ全体がconst参照によって渡されるので、ペアのメンバのconstanceは無関係です; constまたはnon-constのどちらかを許可する必要があります) – Wyzard

+0

マップで構築しようとしましたか?あなたはこれをあまりにも遠くに持っていました – mtb

答えて

5

キーワードconstを直接使用する代わりに、std::add_constを使用する方法もあります。テンプレート経由

往復には、このパラメータの型を経て型推論を防ぐ:

#include <map> 
#include <type_traits> 
#include <utility> 

template< class T1, class T2 > 
void do_stuff(std::pair<T1, T2> const& pair, std::add_const_t<T1>& val) 
{ 
    // Imagine that this does something important... 
    (void) pair; (void) val; 
} 

auto main() 
    -> int 
{ 
    std::map<int, float> foo { { 0, 0.0f } }; 
    do_stuff(*foo.begin(), 0); 
} 
+4

" 'auto main() - > int'"彼のコードの関数名からあまりにも離れた1つの改行文字ですか? – Dani

+1

@Daniあなたは 'int'を意味しますか?' std :: map foo {{0、0.0}}; ' –

+0

@ Cheersandhth.-Alf C++では 'auto main'が許可されていますか? (http://stackoverflow.com/questions/17134975/will-automatic-return-type-deduction-work-for-main) – kfsone

2

本当にエレガントないが、私はあなたが二つの異なるテンプレートparamentersにT1を分ける可能性があるとします。あなたがT1T3との相関関係を課すために、std::enable_if_t経由して、チェックを追加することができます

template <typename T1, typename T2, typename T3> 
void do_stuff(std::pair<T1, T2> const &pair, T3 const &val) { 
    // Imagine that this does something important... 
} 

よう

何か。例によって

template <typename T1, typename T2, typename T3, 
     typename = std::enable_if_t<std::is_same<std::remove_const_t<T1>, std::remove_const_t<T3>>::value>> 
void do_stuff(std::pair<T1, T2> const &pair, T3 const & val) { 
    // Imagine that this does something important... 
} 
+0

これはコンパイルエラーを解決するだけでなく、呼び出し元がまったく異なる無関係の型を渡すことを可能にします。 ( 'T3'が' T1'と互換性がない場合でもコンパイルエラーが発生しますが、エラーは実際の問題が何であるか明確ではない関数の実装のどこかから来ます) – Wyzard

+0

@Wyzard - あなたはSFINAEを介して、 'T1'と' T3'が(ほぼ一定の 'const')等しいことを強制することができます。以前より醜いですが...それに応じて私の答えを変更しました。 – max66

+0

'typename = typename std :: enable_if_t'で2番目の' typename'は必要ありません。これは '_t'エイリアスのポイントです。 – Oktalist

3
template<class T>struct identity{using type=T;}; 
template<class T>using no_deduce=typename identity<T>::type; 

ラップno_deduceにおける第二のタイプは推論をブロックします。

template <typename T1, typename T2> 
void do_stuff(std::pair<T1, T2> const &pair, no_deduce<T1> const &val) { 
    // Imagine that this does something important... 
} 

これはどちらも問題なく動作しています。

ここでT1が参照型で、その場合はconst&の場合は何をしたいか考えてみる価値があります。 がint&であるとすると、int& const&はちょうどint&になります。

これはあなたが望むものではない場合があります。

は、たぶん、あなたが望むもの、あなたが本当に欲しいもの、である:

template <typename T1, typename T2> 
void do_stuff(std::pair<T1, T2> const &pair, std::remove_reference_t<T1> const &val) { 
    // Imagine that this does something important... 
} 

あなたは必ずそれがconstであるあなたが改善する変更されてからvalを防ぐたい場合はダメconst&は、あなたの&を忘れてしまった場合。さて、あなたの貴重なものを浪費しないでくださいconst&remove_reference_tそれはちょうど良いでしょう。

volatileを処理したい場合は、remove_volatile_tを取得する必要があります。 template<class T>using clean_t=std::remove_cv_t<remove_reference_t<T>>で永遠に結びつけてください。あなたがconst&をしたい場合は、単に

template <typename T1, typename T2> 
void do_stuff(std::pair<T1, T2> const &pair, clean_t<T1> const &val) { 
    // Imagine that this does something important... 
} 

const&に簡単にある持っているが、それはそれは方法です。

関連する問題