簡體   English   中英

重載C ++模板化函數

[英]Overloading of C++ templated functions

我認為以下代碼應該工作,但g ++和clang ++都返回完全相同的錯誤(雖然Visual C ++ 2012沒有)。

#include <iostream>
#include <tuple>

template <int N, typename T>
struct A { };

template <typename Tuple>
double result(const Tuple& t, const A<0, typename std::tuple_element<0, Tuple>::type>& a)
{
  return 0;
}

template <typename Tuple>
double result(const Tuple& t, const A<std::tuple_size<Tuple>::value-1,
                                      typename std::tuple_element<std::tuple_size<Tuple>::value-1,Tuple>::type>& a)
{
  return 1;
}

template <typename Tuple, int N>
double result(const Tuple& t, const A<N, typename std::tuple_element<N, Tuple>::type>& a)
{
  return 0.5;
}

int main()
{
  auto a = std::make_tuple(0, 1, 2., 3., 4);
  std::cout << result(a, A<0,int>()) << std::endl;
  std::cout << result(a, A<2,double>()) << std::endl;
  std::cout << result(a, A<4,int>()) << std::endl; // Fails if uncommented
  return 0;
}

該錯誤是由於最后一行以及第二和第三result函數被認為是等效的。 雖然我認為第二個比第三個更合適(就像第一個一樣)。

我不確定。 任何人都可以告訴我,如果我錯了或編譯器是?

在第二個重載中, std::tuple_size<Tuple>::value-1部分取決於模板參數Tuple ,因此不是更好的匹配,或者在C ++中說“更專業”。 這就是為什么它被認為是與第三個顯式具有N重載相等的原因。

只有你的第一個重載使用一個0的常量值,它不依賴於Tuple ,因此是一個更好的匹配。


如果你想解決你的問題,你可以禁用第三個重載何時匹配第二個重載:

template <typename Tuple, int N>
typename std::enable_if< N != std::tuple_size<Tuple>::value-1, double >::type
result(const Tuple& t, const A<N, typename std::tuple_element<N, Tuple>::type>& a)
{
  return 0.5;
}

TLDR; 程序編譯失敗的原因是第二次和第三次重載在重載解析期間同樣匹配良好。 特別是,兩者都不比另一個更專業。 由於重載決策無法選擇最佳匹配,因此程序格式錯誤。 您可以通過SFINAE解決問題。

問題

14.5.6.2函數模板的部分排序[temp.func.order]

2部分排序通過依次轉換每個模板(參見下一段)並使用函數類型執行模板參數推導來選擇兩個函數模板中哪一個比另一個更專業。 演繹過程確定其中一個模板是否比另一個模板更專業。 如果是這樣,則更專業的模板是由部分排序過程選擇的模板。

3要生成轉換模板,對於每種類型,非類型或模板模板參數(包括其模板參數包(14.5.3))分別合成唯一類型,值或類模板,並將其替換為每次出現模板的函數類型中的參數。

對於所有三個重載,第一個合成參數是相等的,並且因為所有參數都是逐個考慮的,所以我們可以關注第二個。

您的第一個重載轉換為以下合成的第二個參數

const A<0, typename std::tuple_element<0, Arg1>::type>&

您的第二個重載轉換為以下合成的第二個參數

const A<
        std::tuple_size<Arg1>::value-1, typename        
        std::tuple_element<std::tuple_size<Arg1>::value-1, Arg1>::type
>&

您的第三個重載轉換為以下合成的第二個參數

const A<Arg2, typename std::tuple_element<Arg2, Arg1>::type>&    

14.8.2.4在部分排序期間推導模板參數[temp.deduct.partial]

2兩組類型用於確定偏序。 對於涉及的每個模板,都有原始函數類型和轉換后的函數類型。 [注意:轉換類型的創建在14.5.6.2中描述。 - 結束注釋]演繹過程使用變換后的類型作為參數模板,將另一個模板的原始類型用作參數模板。 對於部分排序比較中涉及的每種類型,此過程完成兩次:一次使用轉換的模板-1作為參數模板,使用template-2作為參數模板,再次使用轉換的模板-2作為參數模板和模板-1作為參數模板。

很明顯,第一個和第二個重載沒有第二個模板參數來推斷,因此它們至少與第三個重載一樣專用。 問題是第三個是否可以從第一個和第二個重載的合成第二個參數推導出它的N參數。

對於第一個重載,對於N=0 ,這是正確的,因此第一個重載比第三個過載更專業。 這就是您的第一個函數調用選擇第一個重載的原因。

對於第三個重載,不會發生參數推導,它是一個非推導的上下文:

14.8.2.5從類型[temp.deduct.type]中推導出模板參數

5未推斷的背景是:

- ......

- 非類型模板參數或數組綁定,其中子表達式引用模板參數。

- ......

這意味着第三次重載也至少與第二次過載一樣專門。 因此,重載決策無法選擇一個,並且程序格式錯誤。

治愈

只需在enable_if內使用非重疊條件進行兩次重載(使用SFINAE)。 在這種情況下,這會繞過重載分辨率。

template <typename Tuple, int N>
typename std::enable_if<N == std::tuple_size<Tuple>::value-1, double>::type
result(const Tuple& t, const A<N, typename std::tuple_element<N, Tuple>::type>& a)
{
  return 1;
}

template <typename Tuple, int N>
typename std::enable_if<N != std::tuple_size<Tuple>::value-1, double>::type
result(const Tuple& t, const A<N, typename std::tuple_element<N, Tuple>::type>& a)
{
  return 0.5;
}

實例

您應該使用某些標記調度替換您的重載。

編寫一個函數,然后以靜態方式檢查第二個arg A is_same為元組中的第一個類型,調用具有依賴於該類型的類型的另一個函數。 在假分支上重復最后一次。

 helper( t, a, std::is_same<A, std::tuple_element<0, Tuple>>() );

可能有一些decayremove_const在那里。

這個想法是, std::is_same<X,Y>true_type如果它們是相同的,和false_type否則。 helper重載true和false類型的第三個參數,為您提供編譯時分支。 再次重復最后一個類型,你就完成了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM