在撰寫模板化庫時,有時需要在函式模板定義之后實作行為。例如,我正在考慮log一個記錄器庫中的函式,它被實作為
template< typename T >
void log(const T& t) {
std::cout << "[log] " << t << std::endl;
}
以后如果我想log在我自己的型別上使用,我會實作std::ostream& operator<<(std::ostream&, CustomType)并希望該log功能CustomType自動運行。
但是,我非常不確定這種模式是否符合標準。為了了解編譯器如何處理它,我撰寫了以下最小示例。
#include<iostream>
// In some library...
void foo(double) { std::cout << "double" << std::endl; }
template< typename T>
void doFoo(T x) {
foo(x);
}
// In some codes using the library...
struct MyClass {};
template< typename T > struct MyClassT {};
namespace my { struct Class {}; }
void foo(MyClass) { std::cout << "MyClass" << std::endl; }
void foo(MyClassT<int>) { std::cout << "MyClassT<int>" << std::endl; }
void foo(my::Class) { std::cout << "my::Class" << std::endl; }
void foo(int) { std::cout << "int" << std::endl; }
int main() {
doFoo(1.0); // okay, prints "double".
doFoo(MyClass{}); // okay, prints "MyClass".
doFoo(MyClassT<int>{}); // okay, prints "MyClassT<int>".
doFoo(42); // not okay, prints "double". int seems to have been converted to double.
// doFoo(my::Class{}); // compile error, cannot convert my::Class to int.
return 0;
}
我希望doFoo通過多載foo函式來注入函式模板。結果似乎非常不一致,因為它適用于自定義(模板化)型別,但不適用于命名空間或內置型別中的自定義型別。結果與編譯器 MSVC(與 Visual Studio 16.10.1 捆綁在一起)以及 gcc 9.3.0 的結果相同。
我現在很困惑什么應該是正確的行為。我想這與實體化的位置有關。我的問題是:
- 上面的代碼合法嗎?或者他們是畸形的?
- 如果代碼是合法的,是什么導致了不同多載的不一致行為?
- If the codes are illegal, what would be a good alternative to injecting library templates? (I'm thinking of passing functions/functors explicitly to my
doFoofunction, like what<algorithm>is doing.)
uj5u.com熱心網友回復:
如果代碼是非法的,什么是注入庫模板的好選擇?(我正在考慮將函式/函子顯式傳遞給我的
doFoo函式,就像<algorithm>正在做的那樣。)
您可以將函子與默認特征型別結合使用,以獲得“兩全其美”。這基本上就是標準庫中無序容器使用std::hash.
它允許通過專門化特征或顯式傳遞函子來注入。
#include<iostream>
// In some library...
template <typename T>
struct lib_trait;
template<>
struct lib_trait<double> {
void operator()(double) const { std::cout << "double" << std::endl; }
};
template<typename T, typename CbT=lib_trait<T>>
void doFoo(T x, const CbT& cb={}) {
cb(x);
}
// In some codes using the library...
struct MyClass {};
template< typename T > struct MyClassT {};
namespace my { struct Class {}; }
template<>
struct lib_trait<MyClass> {
void operator()(MyClass) const { std::cout << "MyClass" << std::endl; }
};
template<>
struct lib_trait<MyClassT<int>> {
void operator()(MyClassT<int>) const { std::cout << "MyClassT<int>" << std::endl; }
};
template<>
struct lib_trait<int> {
void operator()(int) const { std::cout << "int" << std::endl; }
};
int main() {
// Leverage default argument to get the same syntax.
doFoo(1.0); // okay, prints "double".
// Handled by specializations defined later.
doFoo(MyClass{}); // okay, prints "MyClass".
doFoo(MyClassT<int>{}); // okay, prints "MyClassT<int>".
doFoo(42); // okay, prints "int".
// Pass in an explicit functor.
doFoo(my::Class{}, [](const auto&){});
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/337798.html
上一篇:C 程式未進入“for”回圈
