2017-09-13 34 views
3

std::tupleの参照基底クラスを受け取る関数に、引数として参照派生クラスのstd::tupleを渡そうとすると、テンプレート引数の減算が失敗します。なぜコンパイラはテンプレート引数T1T2を推測できないのですか?どのように修正できますか?std :: tupleとテンプレート派生クラスでテンプレート引数の控除/置換が失敗するのはなぜですか?

// Example program 
#include <iostream> 
#include <tuple> 

template<typename T> 
struct Base {}; 

template<typename T> 
struct Derived1 : Base<T> {}; 

template<typename T> 
struct Derived2 : Base<T> {}; 

template<typename T1, typename T2> 
void function(std::tuple<Base<T1>&,Base<T2>&> arg) 
{ 
    std::cout << "Hello\n"; 
} 

int main() 
{ 
    Derived1<int> d1; 
    Derived2<double> d2; 

    //function(std::tie(d1, d2)); /* In this case the template argument deduction/substitution failed */ 
    function<int,double>(std::tie(d1, d2)); /* here works */ 

    Base<int>& b1 = d1; 
    Base<double>& b2 = d2; 

    function(std::tie(b1, b2)); /* but, in this case also works */ 
} 

これは、ラインコードfunction(std::tie(d1, d2));のためのコンパイルエラーです:

In function 'int main()': 
25:30: error: no matching function for call to 'function(std::tuple<Derived1<int>&, Derived2<double>&>)' 
25:30: note: candidate is: 
15:6: note: template<class T1, class T2> void function(std::tuple<Base<T>&, Base<T2>&>) 
15:6: note: template argument deduction/substitution failed: 
25:30: note: mismatched types 'Base<T>' and 'Derived1<int>' 
25:30: note: 'std::tuple<Derived1<int>&, Derived2<double>&>' is not derived from 'std::tuple<Base<T>&, Base<T2>&>' 
+0

答えは基本的に控除が嫌いです。 'Base &'の引数に 'Double &'を導くことは幸いではありません。私は、誰かに標準的な習得者が実際の答えを書くのをもっと堪能にさせます。 – cdhowie

+0

'std :: tuple 'は、std :: tuple とは異なり、別のクラスです。 2つの間に暗黙的な変換はありません。ユーザー定義の変換はありますが、テンプレート引数の控除の際には考慮されません。 –

+0

@cdhowie 'Derived1 'のlvalue引数から' Base & 'を推論するのは実際にはうまくいきます。関数の引数がクラス型の場合、直接減算が失敗した場合でも引数型の基底クラスも考慮されるからです。しかし、ここでの問題は、引数型自体にのみ適用され、 'std :: tuple'の内部で使用されるテンプレートパラメータ/引数のような他のコンテキストはありません。あるいは、別の方法で見ると、2つの 'tuple'型の関係は、ユーザ定義の変換であり、導出 - ベースの関係ではありません。 – aschepler

答えて

1

控除は、このように動作しません。それは変換の前に引っ張り出します。ここでコンパイラはBase<T>から、Tを推測し、DerivedN<T>を渡そうとしています。彼らはタイプシステムの観点から全く異なる獣であり、その機能は呼出しに適したものを見つけるために捨てられる。
エラーを見て、それはかなり明確です。

どのように修正できますか?

#include<type_traits> 

// ... 

template<template<typename> class C1, template<typename> class C2, typename T1, typename T2> 
std::enable_if_t<(std::is_base_of<Base<T1>, C1<T1>>::value and std::is_base_of<Base<T2>, C2<T2>>::value)> 
function(std::tuple<C1<T1>&, C2<T2>&> arg) 
{ 
    std::cout << "Hello\n"; 
} 

// ... 

はそれを参照してくださいwandbox上で実行されている:

あなたがそれらを受け入れており、まだ彼らはBaseから派生しているという事実を強制するために、このようなものを使用することができます。

+0

素晴らしい回避策です。これは 'std :: is_base_of <>' whit pack展開のために[this answer](https://stackoverflow.com/a/13563429/5631903)を使ってパラメータパックに展開できます。 [ここにコードを見てください](http://cpp.sh/6otoi) – Solrac

+0

もう1つのことですが、 'std :: enable_if_t <>'はC++ 14以上のヘルパーです。 C++ 11では 'typename std :: enable_if <> :: type' :)を使うことができます – Solrac

関連する問題