问题描述
我有一个非常具体的情况,我将一堆数据提供给一个类似 hasher 的类。特别是,我使用的一种数据类型有一个成员,其类型取决于超类型的类型参数。长话短说,这里有一段代码说明了这种行为:
#include <assert.h>
#include <iostream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
// Some dummy priority structs to select overloads
struct priority0 { };
struct priority1 : priority0 { };
// This is the hasher-like function
struct Catcher
{
// Ideally we Feed everything to this object through here
template <typename T> Catcher& operator<<(const T& v)
{
add(v,priority1{}); // always attempt to call the highest-priority overload
return *this;
}
// For floating-point data types
template <typename T> auto add(const T& v,priority1) -> std::enable_if_t<std::is_floating_point_v<T>,void>
{
std::cout << "caught float/double : " << v << std::endl;
}
// For ranges
template <class T> auto add(const T& range,priority1) -> decltype(begin(range),end(range),void())
{
for(auto const& v : range)
*this << v;
}
// For chars
void add(char c,priority1)
{
std::cout << c;
std::cout.flush();
}
// When everything else fails ; ideally should never happen
template <typename T> void add(const T& v,priority0)
{
assert(false && "should never happen");
}
};
// The one data type. Notice how the primary template and the
// specialization have a `range` member of different types
template <class T> struct ValueOrRange
{
struct Range
{
T min;
T max;
};
Range range;
T value;
};
template <> struct ValueOrRange<std::string>
{
std::vector<std::string> range;
std::string value;
};
// Overload operator<< for Catcher outside of the
// class to allow for processing of the new data type
// Also overload that for `ValueOrRange<T>::Range`. SFINAE should make sure
// that this behaves correctly (?)
template <class T> Catcher& operator<<(Catcher& c,const typename ValueOrRange<T>::Range& r)
{
return c << r.min << r.max;
}
template <class T> Catcher& operator<<(Catcher& c,const ValueOrRange<T>& v)
{
return c << v.range << v.value;
}
int main(int argc,char *argv[])
{
ValueOrRange<std::string> vor1{{},"bleh"};
ValueOrRange<float> vor2{{0.f,1.f},0.5f};
Catcher c;
c << vor1; // works fine,displays "bleh"
c << vor2; // fails the assert in Catcher::add(const T&,priority0) with T = ValueOrRange<float>::Range
return 0;
}
虽然行 c << vor1
通过各种重载正确解析并具有预期效果,但第二行 c << vor2
未通过断言。
- 我想要发生的事情:
c << vor2
调用Catcher& operator<<(Catcher& s,const ValueOrRange<float>& v)
,后者又调用Catcher& operator<<(Catcher& s,const typename ValueOrRange<float>::Range& r)
- 会发生什么:不是
Catcher& operator<<(Catcher& s,const typename ValueOrRange<float>::Range& r)
,而是Catcher& Catcher::operator<<(const T& v)
和T = typename ValueOrRange<float>::Range
被调用,因此断言失败。
值得注意的是,同样的代码在 MSVC 上具有预期的效果,但在 GCC 上没有断言。
知道我应该如何解决这个问题吗?
解决方法
在 Catcher& operator<<(Catcher& c,const typename ValueOrRange<T>::Range& r)
中,T
在不可推论中。
一种解决方法是 friend
函数:
template <class T> struct ValueOrRange
{
struct Range
{
T min;
T max;
friend Catcher& operator<<(Catcher& c,const Range& r)
{
return c << r.min << r.max;
}
};
Range range;
T value;
};