英文:
How do I specialize a templated function for types that have a particular method name?
问题
以下是您要翻译的内容:
A third-party library provides us with a function that looks something like this (obviously the actual function is much more complicated):
template<typename T>
std::string toString(const T& value) {
std::cout << "Called 'unspecialized' toString" << std::endl;
return std::to_string(value);
}
Because this is from a third-party libary, I am unable to change this function signature.
Many of our internal types have a toString
method, for example:
struct Foo {
std::string toString() const {
return "Foo";
}
};
Ideally, calling the toString
function would defer to the internal type's toString
method if it has one. I have tried to accomplish this using SFINAE:
template<typename T, typename Enable = decltype(std::declval<T>().toString())>
std::string toString(const T& value) {
std::cout << "Called 'specialized' toString" << std::endl;
return value.toString();
}
However, calling toString
for such types now results in a compilation error:
int main() {
toString(5); // OK
toString(Foo{}); // compilation error: call of overloaded 'toString(Foo)' is ambiguous
}
How do I resolve this error without changing the unspecialized version of toString
?
Edit: for some background, this is an issue with Google Test's PrintTo
function, which prints out values from unit tests (doc). That function is designed to look for an overload for a particular type. If it finds one, it uses that overload to print the type. If it doesn't find one, it uses its default ('unspecialized') implementation, which just dumps out the bytes.
There were several answers that offered solutions which required changing the default implementation of the function. These answers may be helpful to other users, but they do not help this case. I marked Paul's concept-based answer as accepted since it seems to be the only one that works without requiring modification of the default signature, even though it requires C++20.
I think this issue may be more accurately categorized as a limitation with Google Test. I found at least one issue in GitHub that addresses this, but it's still open.
英文:
A third-party library provides us with a function that looks something like this (obviously the actual function is much more complicated):
template<typename T>
std::string toString(const T& value) {
std::cout << "Called 'unspecialized' toString" << std::endl;
return std::to_string(value);
}
Because this is from a third-party libary, I am unable to change this function signature.
Many of our internal types have a toString
method, for example:
struct Foo {
std::string toString() const {
return "Foo";
}
};
Ideally, calling the toString
function would defer to the internal type's toString
method if it has one. I have tried to accomplish this using SFINAE:
template<typename T, typename Enable = decltype(std::declval<T>().toString())>
std::string toString(const T& value) {
std::cout << "Called 'specialized' toString" << std::endl;
return value.toString();
}
However, calling toString
for such types now results in a compilation error:
int main() {
toString(5); // OK
toString(Foo{}); // compilation error: call of overloaded 'toString(Foo)' is ambiguous
}
How do I resolve this error without changing the unspecialized version of toString
?
Edit: for some background, this is an issue with Google Test's PrintTo
function, which prints out values from unit tests (doc). That function is designed to look for an overload for a particular type. If it finds one, it uses that overload to print the type. If it doesn't find one, it uses its default ('unspecialized') implementation, which just dumps out the bytes.
There were several answers that offered solutions which required changing the default implementation of the function. These answers may be helpful to other users, but they do not help this case. I marked Paul's concept-based answer as accepted since it seems to be the only one that works without requiring modification of the default signature, even though it requires C++20.
I think this issue may be more accurately categorized as a limitation with Google Test. I found at least one issue in GitHub that addresses this, but it's still open.
答案1
得分: 2
In C++20,这很容易。只需用以下代码替换您的SFINAE部分:
template<typename T>
concept HasToString = requires(T t) { t.toString(); };
template<typename T> requires HasToString<T>
std::string toString(const T& value){
std::cout << "Called 'specialized' toString" << std::endl;
return value.toString();
}
看看@TedLyngmo在评论中提供的更好的解决方案。
这是我尝试的一个C++17解决方案。
可以考虑将问题反过来,用以下代码替换您的SFINAE部分(从来不是我最喜欢的东西),以仅选择算术类型的内置to_string
(根据需要调整该测试以适应)if constexpr
使这特别简洁:
template<class T>
std::string toString (const T& value)
{
if constexpr (std::is_arithmetic_v<T>)
{
std::cout << "Called 'unspecialized' toString" << std::endl;
return std::to_string (value);
}
else
{
std::cout << "Called 'specialized' toString" << std::endl;
return value.toString ();
}
}
英文:
In C++20 this is easy. Just replace your SFINAE stuff with this:
template <typename T>
concept HasToString = requires(T t) { t.toString(); };
template<typename T> requires HasToString <T>
std::string toString(const T& value){
std::cout << "Called 'specialized' toString" << std::endl;
return value.toString();
}
See also @TedLyngmo's (better) offering in the comments.
Here's my attempt at a C++17 solution.
Turn the problem on its head and replace your SFINAE stuff (never one of my favourite things) with this, in order to select the built-in to_string
for arithmetic types only (tweak that test to suit). if constexpr
makes this particularly neat:
template<class T>
std::string toString (const T& value)
{
if constexpr (std::is_arithmetic_v <T>)
{
std::cout << "Called 'unspecialized' toString" << std::endl;
return std::to_string (value);
}
else
{
std::cout << "Called 'specialized' toString" << std::endl;
return value.toString ();
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论