下面的代碼顯示了一些有趣的行為:
使用 命名空間 std.com.cn>。
template<class T>
class B{
public:
void foo(B<T> &x)const。
template<class F> void foo(F f)。
};
template<typename T> void B<T> :: foo(B<T> &x)const{cout<<"foo_B"<<endl;}。
template<typename T> template<typename F> void B< T> 。 :foo(F f){cout<<"foo_F"<<endl;}。
int main(){
B<int> a;
B<int> b.
b.foo(a)。
b.foo([](){;})。
return(0)。
我的預期輸出是
。foo_B
foo_F
但實際輸出是
foo_F
foo_F
這取決于void foo(B<T> &x)是否被宣告為const。如果const被省略了,那么輸出就會像預期的那樣。
此外,如果const被添加到void foo(F f)中,輸出結果也和預期一樣。
然而,void foo(B<T> &x)將不會改變this,而void foo(F f)將改變this。因此,當前的布局是需要的。
有什么想法嗎?
如果有任何想法可以在不洗掉const的情況下解決這個問題,非常感謝。
uj5u.com熱心網友回復:
這里的問題是,由于void foo(B<T> &x)const;是const限定的,它將不得不限定你正在呼叫函式的物件。 這并不像template<class F> void foo(F f);提供的那樣完全匹配,因為它不需要做const限定。 這就是為什么它被用于這兩個呼叫的原因。
你可以通過對模板版本進行const限定來解決這個問題,比如:
#include <iostream>
使用 命名空間 std.com.cn>。
template<class T>
class B{
public:
void foo(B<T> &x)const。
template<class F> void foo(F f)const。
};
template<typename T> void B<T> :: foo(B<T> &x)const{cout<<"foo_B"<<endl;}。
template<typename T> template<typename F> void B< T> 。 :foo(F f)const{cout<<"foo_F"<< endl;}。
int main(){
B<int> a;
B<int> b.
b.foo(a)。
b.foo([](){;})。
return(0)。
這將列印出
foo_B
foo_F
另一個選擇是使用SFINAE來約束模板版本,使其不包括B<T>的。 這將看起來像
#include <iostream>
使用 命名空間 std.com.cn>。
template<class T>
class B{
public:
void foo(B<T> &x)const。
template<class F, std::enable_if_t< ! std::is_same_v<B<T>, F>, bool> = true>
void foo(F f)。
};
template<typename T> void B<T> :: foo(B<T> &x)const{cout<<"foo_B"<<endl;}。
template<typename T> template<class F, std: :enable_if_t<! std::is_same_v<B<T>, F>, bool> >
void B<T>::foo(F f){cout<<"foo_F"<<endl;}。
int main(){
B<int> a;
B<int> b.
b.foo(a)。
b.foo([](){;})。
return(0)。
并且有與第一個例子相同的輸出。
uj5u.com熱心網友回復:
限定轉換(這里:在隱含的物件引數上)不是身份轉換,并且在多載決議排名上有代價
。成員函式void foo(B<T>& x) const是const限定的,而模板成員函式template<class F> void foo(F f)則不是。這意味著后者與隱含物件引數為非常數的呼叫更匹配,根據[over.ics.rank]/3.2.5:
標準轉換序列S1是一個更好的轉換序列,而不是 標準轉換序列S2更好,如果
- [...],或者,如果不是這樣,S1和S2只在它們的限定轉換([conv.qual])中有所不同,并且產生類似的型別T1和T2。 分別產生類似的型別T1和T2,其中T1可以通過資格轉換轉化為T2。 轉換。
如果你在main中const-限定了自動變數b,非模板多載將被選擇:
// ^^^ `foo`多載,如OP的例子。
B<int> a{};
B<int> const b{}.
b.foo(a); // foo_B
如果你改用const質量的模板成員函式foo,那么非模板將與模板多載相同的匹配(隱含物件引數需要const-qualification),在這種情況下,非模板函式被選為最佳可行的多載,按照[over.match.best]/2.4。
如果你從不希望某個特定的多載參與型別謂詞的多載決議:移除它
。然而,在實際應用中,b不能被宣告為const,這歸結為做一個拷貝,比如const c(b); 然后使用c.foo(a)
。
當其型別模板引數的模板引數是B類模板的特殊化時,你可以使用trait來移除模板成員函式:
#include <iostream>
#include <type_traits>
template <class T, template <class. .> class Primary>
struct is_specialization_of : std::false_type {};
template < template < class. .> class Primary, class. Args>
struct is_specialization_of<Primary<Args...>, Primary> : std::true_type {};
template <class T, template <class。 .> class Primary>
inline constexpr bool is_specialization_of_v{is_specialization_of<T, Primary> ::value}。
template <class T> class B {
public:
void foo(B<T> &x) const< std: :cout << "foo_B" << std::endl; }
template <class F, typename = std::enable_if_t< ! is_specialization_of_v<F, B>>>。
void foo(F f) {
std::cout << "foo_F" << std::endl;
}
};
int main() {
B<int> a;
B<int> b.
b.foo(a)。
b.foo([] () {; });
return(0)。
在這里我們利用了P2098R1的is_specialization_of特質(只是注意到這對于作為別名模板的模板引數有實施差異--有點沒有明確規定IIRC)。
請注意,對于 uj5u.com熱心網友回復: 一個相當簡單的方法,即使有點啰嗦,就是當你想呼叫const版本的時候,把物件投給const: 這就足以讓呼叫顯示
標籤: 上一篇:做......而回圈不終止
下一篇:為什么比較結果與預期不同?
B的另一個特殊化的引數(比隱式物件引數的特殊化),這種方法沒有任何多載是可行的。
static_cast<const B<int>>(b)。 foo(a)。
foo_B...
