2016-02-18 63 views
4

試着追蹤返回類型和標籤調度,我寫了下面的代碼。追尾返回類型和標籤調度

#include <string> 
#include <iostream> 

using namespace std; 

namespace Params 
{ 
struct t_param1{}; 
struct t_param2{}; 
}; 

template<typename t_detail> 
struct Select; 

template<> 
struct Select<Params::t_param1> {using choice = Params::t_param1;}; 

template<> 
struct Select<Params::t_param2> {using choice = Params::t_param2;}; 

class Tester 
{ 
private: 
    using t_uint32 = uint32_t; 
    using t_string = string; 

private: 
    t_uint32 m_param1; 
//  t_string m_param2; 

private: 
    template<typename t_entity> 
    void assign(const Params::t_param1&, t_entity&& entity); 

    template<typename t_entity> 
    void assign(const Params::t_param2&, t_entity&& entity); 

    auto access(const Params::t_param1&) -> decltype(m_param1); 
//  auto access(const Params::t_param2&) -> decltype(m_param2); 


public: 
    template<typename t_detail, typename t_entity> 
    void assign(t_entity&& entity); 

    template<typename t_detail> 
    auto access() -> decltype(access(typename Select<t_detail>::choice())); 
}; 

template<typename t_detail, typename t_entity> 
void 
Tester::assign(t_entity&& entity) 
{ 
assign(typename Select<t_detail>::choice(), entity); 
} 


template<typename t_entity> 
void 
Tester::assign(const Params::t_param1&, t_entity&& entity) 
{ 
m_param1 = entity; 
cout << "Assigned m_param1 with " << entity << endl; 
} 

/* 
template<typename t_entity> 
void 
Tester::assign(const Params::t_param2&, t_entity&& entity) 
{ 
m_param2 = entity; 
cout << "Assigned m_param2 with " << entity << endl; 
} 
*/ 



template<typename t_detail> 
auto 
Tester::access() 
-> decltype(access(typename Select<t_detail>::choice())) 
{ 
return(access(typename Select<t_detail>::choice())); 
} 

auto 
Tester::access(const Params::t_param1&) 
-> decltype(m_param1) 
{ 
return(m_param1); 
} 

/* 
auto 
Tester::access(const Params::t_param2&) 
-> decltype(m_param2) 
{ 
return(m_param2); 
} 
*/ 

int main() { 
auto tester = Tester(); 
tester.assign<Params::t_param1>(79); 
// tester.assign<Params::t_param2>("viziv"); 

auto param1 = tester.access<Params::t_param1>(); 
// auto param2 = tester.access<Params::t_param2>(); 

cout << "Access: param1 = " << param1 << endl; 
// cout << "Access: param2 = " << param2 << endl; 

return 0; 
} 

當我編譯使用蘋果的LLVM版本7.0.2(鐺-700.1.81)這段代碼中,我得到以下編譯錯誤

junk1.cpp:78:9: error: out-of-line definition of 'access' does not match any declaration in 'Tester' 
Tester::access() 
     ^~~~~~ 
1 error generated. 

奇怪的是,當我取消註釋代碼分配和訪問param2(在上面的代碼中註釋掉),它編譯得很好併產生所需的結果。

我在做什麼錯?任何人都可以向我解釋爲什麼在編譯行爲中包含param2變化?

+0

嗯...不確定。如果將問題函數的定義移動到類定義中,似乎可行,但我無法解釋_why_解決了問題。請參閱http://ideone.com/1XMZrN。 ......如果我們使用'decltype(auto)'作爲問題函數的返回類型,即使定義不在類定義之外,也沒有尾隨返回類型,它似乎也能工作。 –

+0

這是一個_minimal_ reproducer嗎? –

+0

@PreferenceBean我這麼認爲。我只有兩個公共成員可以分配和一個訪問,而私有函數實現標籤分派。 – vixiv

回答

0

我認爲這裏有一個半問題。

首先在於使用尾隨返回類型本質上創建了模板化函數。當您嘗試使用類的函數時,類類型不能是不完整的。

這就是爲什麼將公開的access方法的函數定義移入類聲明可修復它的原因(Demo);只要公衆access方法尚未定義,該類別不完整,並且只有在類別完成後才能定義該方法。

請注意,另一種解決此問題的方法是,如果專用版本access在某種程度上是非成員函數(例如,周圍範圍內的自由浮動函數)。

該方法的問題(一半的問題,因爲你實際上並沒有試圖這樣做)是,試圖調用現在免費的浮動版本access需要編譯器評估所有可能的重載,包括公共模板access(感謝ADL)。當發生這種情況時,Select<t_detail>::choice將在non-deduced context中進行評估,並且無法獲得實際的基礎類型。

所以,如果我們都感動的Tester私人access更名爲(以類似access2),然後我們被允許的聲明和公衆access函數的定義分離(Demo

+0

還有什麼讓我困惑的是,不管使用什麼編譯器,不完整的類型都是不完整的類型,不應該被編譯。對?那爲什麼gcc,clang和vC++的行爲方式有很大不同? – vixiv

+0

同意這很奇怪。純粹的猜測,但培訓返回類型的實現是非常新的,我知道微軟的解析器與gcc或clang(現在)完全不同。這些實現是關於它是否適用於不完整的類型或者不足以改變。 – AndyG