2011-07-04 37 views
2

我可以看到介紹我的問題的唯一方法,就是首先提供一個例子:隱式轉換操作符隱藏規則

template<typename T> 
class foo 
{ 
    public: 
     foo() 
     {} 

     foo(const foo&) 
     {} 
}; 

template<template<typename> class C> 
class convertible_to 
{ 
    public: 
     template<typename T> 
     operator C<T>(); 
}; 

class convertible_to_foo : public convertible_to<foo> 
{ 
     public: 
     template<typename T> 
     operator foo<T>() 
     { 
      return foo<T>(); 
     } 
}; 

我會想到的是,隱式轉換操作符fooconvertible_to_foo宣佈將隱藏,即過載時,隱式轉換到操作者在foo聲明convertible_to,但GCC 4.6未能接受以下行

convertible_to_foo f; 
foo<int> ff(f); 

和抱怨從轉換至foo<int>不明確。 這是預期的行爲還是GCC在這裏可能是錯的?

謝謝你閱讀這個問題。

編輯

要理解爲什麼我想用這樣一個顯然不可思議的技術,請參考下面的karrek針對我的意見,並採取看看下面的用例:

考慮以下類:

template<typename TYPE> 
class real; 

template<typename TYPE> 
class complex; 

我正在設計的方式來最大限度地減少由於價值調節,即域錯誤的異常。例如,不是允許發生錯誤,而是將sqrt函數應用於類real的對象,將始終返回類型爲complex的對象。

到目前爲止好,但現在有一些預先定義的常數,如pi或複雜的我是一個好主意。最簡單的方法顯然是宣佈他們如下:

real<double> pi = 3.14...; 

不過,作爲一個(也許是太)完美主義的程序員,我意識到,這種方法有兩個問題:

1 - 應用程序這要求高精度的pi,例如,不會受益於使用實際的

2 - 關注內存使用的應用程序將無法使用此pi,因爲使用real類型的對象操作它會產生類型爲real的對象。 (好吧,每次操作發生時明確強制轉換爲真實是一種我想避免的醜)

我看到解決這個問題的最聰明的方法是設計常量,通過隱式轉換運算符懶惰地正確評估自己:

template<template<typename> class C> 
class scalar_constant 
{ 
    public: 
     scalar_constant& operator = (const scalar_constant&) = delete; 

     template<typename T> 
     operator C<T>() const; 
}; 

class pi_t : public scalar_constant<real>, public scalar_constant<complex> 
{ 
    public: 
     template<typename T> 
     operator real<T>() const 
     { 
      return {std::acos(static_cast<T>(-1))}; 
     } 

     template<typename T> 
     operator complex<T>() const 
     { 
      return {std::acos(static_cast<T>(-1))}; 
     } 
}; 

const pi_t pi = pi_t(); 

這裏這個「概念檢查」是絕對重要的,因爲我不會爲每一個我決定提供的常量重載每個運算符。這樣,我可以簡單地爲操作員提供啓用SFINAE的重載,並且只需要繼承「概念」即可提供新的常量。

我明顯地可以清除基類中未定義的轉換運算符,並且問題會得到解決,但是它會丟失整個概念思想,這是通過使程序潛在地不可鏈接到應用來執行概念檢查到未定義的繼承函數,以及讓程序員(懶惰的我)從現在開始回到這個代碼更容易,並且能夠添加另一個符合概念的類,只需查看概念本身並意識到應該提供什麼。

+0

爲什麼要派生而不是爲'foo'使'convertible_to'部分專精? –

+0

@kerrek,這個想法是通過使用由C++ 0x std lib提供的元函數is_base_of來實現某種概念檢查,以便如果我想啓用/禁用一個模板函數/類,該模板函數/類期望TYPE可以轉換爲任何,我可以通過檢查是否convertible_to 是TYPE的基礎來使用SFINAE。如果TYPE確實來自convertible_to ,但不要重載轉換運算符,那麼代碼將會編譯但顯然不會鏈接,這使得它成爲一個非常有效的概念檢查技術。 – brunocodutra

+0

你能發佈一個預期的用例嗎? 'std :: is_convertible'會有什麼用處? –

回答

2

我會想到的是,隱式轉換運營商在convertible_to_foo美孚宣佈將隱藏,即超載,

這混淆了條件。到隱藏意味着當您查看派生類時,只能看到派生的轉換函數而不是基數。到過載意味着你會看到都有的功能。如果派生函數具有與基函數相同的名稱,則派生函數將隱藏基函數。

轉換函數名稱的規則是:如果它們轉換爲相同類型,則它們中的兩個是相同的。所以,如果在基地中你有operator int,並且在派生類中,那麼派生的函數將隱藏基地的一個。如果派生的將是operator float,那麼你沒有隱藏。您也沒有重載,但是如果您在派生類中查找轉換函數operator int,那麼您會找到基類的一個,因爲它沒有被具有相同名稱的派生函數隱藏。

對於模板,它的工作原理相同

struct A { 
    template<typename T, typename U = int> 
    operator T(); 
}; 

struct B : A { 
    template<typename T = int, typename U> 
    operator U(); 
}; 

在這種情況下,如果你嘗試B轉換爲int,你會得到一個含糊不清,因爲這兩個功能將被採取和兩個功能最終會到在完成模板參數推導後,轉換爲intT是與U不同的類型 - 第一個是第一個位置的模板參數,第二個是第二個位置的模板參數,因此不會發生隱藏)。

就你而言,它很相似,但我不同意GCC的行爲。實例化convertible_to<foo>將實例化模板的成員聲明,​​和一個成員會碰巧

template<typename T> 
operator foo<T>(); // after substituting "foo" for "C" 

在派生類中的轉換功能轉換成一模一樣的類型,所以我不認爲GCC應該產生一個含糊不清。

+0

Clang與我同意並接受您的代碼。 –

+0

雖然我在這裏混淆了隱藏和超負荷這個詞,但這正是我對這種情況的理解,因此這就是我期望的行爲。 你似乎並不確定GCC的行爲,但是由於你提供了這個鏗鏘聲的論點,我會把它作爲回答,並向GCC提交一個錯誤報告 (btw,讓我更好地瞭解clang) – brunocodutra

0

回答你的問題的第一部分。在此代碼

template<template<typename> class C> 
class convertible_to 
{ 
    public: 
        template<typename T> 
        operator C<T>(); 
}; 

class convertible_to_foo : public convertible_to<foo> 
{ 
        public: 
        template<typename T> 
        operator foo<T>() 
        { 
            return foo<T>(); 
        } 
}; 

我們真的不能指望operator C<T>應該如果你從convertible_to<foo>繼承,如果你已經從convertible_to<bar>繼承被隱藏,但可見。

這將使C++和模板比現在更加複雜。所以沒有這樣的規則。