我正在研究一个小型编译器,我使用boost::variant<bool, ClassA> info
来存储每个节点的信息。
boost::variant
时, <<
会自动调用特定类型的正确std::cout<< node.info;
运算符
但是,由于ostream
的内置格式化功能无法满足我的要求(如果#t
打印1
而不是node.info==true
并打印"string"
}而不是string
),应引入新类型的bool
/ string
。
我想实现一个模板类Wrapper<T>
,其行为就像T
一样(因为有很多旧代码),并提供{{1}的接口}。
首先,实施了以下版本:
<<
此版本适用于template<typename T> class Wrapper : public T
{
public:
template<typename ... U> Wrapper(const U& ... a):T(a...) {}
friend std::ostream& operator <<(std::ostream& o, const Wrapper<T>& w);
};
,但是当T = bool时,由于内置类型无法继承,因此会出现错误。
我目前的解决方法是使用部分专业化:
std::string
显然,这不是一个完美的解决方案,因为我必须包装template<typename T, bool ISCLASS= std::is_class<T>::value> class Wrapper;
template<typename T> class Wrapper<T, false>
{
private:
T inner;
public:
template<typename ... U> Wrapper(const U& ... a): inner(a...) {}
//Wrap the operators (= + += ...)
template<typename U> Wrapper<T> operator !() { Wrapper<T> res(*this); res.inner=!res.inner; return res; }
operator T() const{ return inner; }
friend std::ostream& operator <<(std::ostream& o, const Wrapper<T>& w);
};
template<typename T> class Wrapper<T, true> : public T
{
public:
template<typename ... U> Wrapper(const U& ... a):T(a...) {}
friend std::ostream& operator <<(std::ostream& o, const Wrapper<T>& w);
};
或任何其他内置类型的每个运算符。
任何帮助都将不胜感激。
感谢。
答案 0 :(得分:3)
我们可以考虑更简单的事情吗?
使用引用或指针创建一个简单的包装器。
template <class T>
struct MyFormattingWrapper
{
const T& nucleus;
};
然后是它的工厂功能。
template <class T>
MyFormattingWrapper<T> my_formatting(const T& n)
{
return MyFormattingWrapper<T>{ n };
}
然后,您可以专门为每种类型设置格式。
std::ostream& operator << (std::ostream& o, const MyFormattingWrapper<int>& w)
{
return o << "int:" << w.nucleus;
}
std::ostream& operator << (std::ostream& o, const MyFormattingWrapper<std::string>& w)
{
return o << "std::string:" << w.nucleus;
}
int main()
{
std::cout << my_formatting(123) << std::endl;
std::cout << my_formatting(std::string{ "abc" }) << std::endl;
}
更新
C-string可能是一种特殊情况。但这并不难。
struct MyFormattingWrapper_c_string
{
const char* const nucleus;
};
MyFormattingWrapper_c_string my_formatting(const char* n)
{
return MyFormattingWrapper_c_string{ n };
}
MyFormattingWrapper_c_string my_formatting(char* n)
{
return MyFormattingWrapper_c_string{ n };
}
std::ostream& operator << (std::ostream& o, const MyFormattingWrapper_c_string& w)
{
return o << "c-string:" << w.nucleus;
}
答案 1 :(得分:2)
Nicky C的答案很棒,但是有一个问题是部分专业化的功能不正常。这意味着您无法生成适用于此类常规向量的版本:
template<typename T>
std::ostream& operator << (std::ostream& o, const MyFormattingWrapper<std::vector<T>>& vec)
{
o << "vec:[ "
for(auto v : vec) {
o<<my_formatting(v);
o<<" ";
}
return o<<"]"
}
您可以通过将专业输出的核心放入MyFormattingWrapper
类并且只有一个operator<<
// The default one
template <class T> struct MyFormattingWrapper {
const T& nucleus;
ostream& output(ostream & os) {
return os<<nucleus;
}
};
// Specialized for string
template <> struct MyFormattingWrapper<std::string> {
const std::string& nucleus;
ostream& output(ostream & os) {
return os<<"string:"<<nucleus;
}
};
// Specialized for vector
template <class T> struct MyFormattingWrapper<std::vector<T>> {
const std::vector<T>& nucleus;
ostream& output(ostream & os) {
os<<"vec:[";
for(auto & v: nucleus) {
os<<my_formatting(v)<<" ";
}
return os<<"]";
}
};
// Now there's just one of these, so partial template
// specialization doesn't cause us any problems
template<typename T>
std::ostream& operator << (std::ostream& os, const MyFormattingWrapper<T>& w) {
return w.output(os);
}
答案 2 :(得分:1)
也许我最好就boost::variant
另一个答案进行跟进。
首先,向@MichaelAnderson学习,并考虑与boost::variant
的互操作性,我想改进包装器的设计。我们添加一个构造函数来启用从nucleus类型到包装器类型的类型转换。
template <class T>
class MyFormatting;
template <class T>
MyFormatting<T> my_formatting(const T& n)
{
return MyFormatting <T>{n};
}
// int
template <>
class MyFormatting<int>
{
private:
const int& nucleus;
public:
MyFormatting(const int& n) : nucleus(n) {}
friend std::ostream& operator << (std::ostream& os, const MyFormatting& w)
{
return os << "int:" << w.nucleus;
}
};
// std::string
template <>
class MyFormatting<std::string>
{
private:
const std::string& nucleus;
public:
MyFormatting(const std::string& n) : nucleus(n) {}
friend std::ostream& operator << (std::ostream& os, const MyFormatting& w)
{
return os << "std::string:" << w.nucleus;
}
};
// c-string
template <>
class MyFormatting<char*>
{
private:
const char* nucleus;
public:
MyFormatting(const char* n) : nucleus(n) {}
friend std::ostream& operator << (std::ostream& os, const MyFormatting& w)
{
return os << "c-string:" << w.nucleus;
}
};
MyFormatting<char*> my_formatting(const char* n)
{
return MyFormatting<char*>{n};
}
// std::vector
template <class T>
class MyFormatting<std::vector<T>>
{
private:
const std::vector<T>& nucleus;
public:
MyFormatting(const std::vector<T>& n) : nucleus(n) {}
friend std::ostream& operator << (std::ostream& os, const MyFormatting& w)
{
os << "std::vector:[";
for (const auto& x : w.nucleus)
{
os << x << " ";
}
os << "]";
return os;
}
};
接下来,让我们使用boost::variant
的包装器。包装器的构造函数可以在nuclues类型的变体与包装器的变体之间进行转换。
boost::variant<int, std::string> var_int = 50;
boost::variant<int, std::string> var_str = "fifty";
boost::variant<MyFormatting<int>, MyFormatting<std::string>> var_fmt_int = var_int;
boost::variant<MyFormatting<int>, MyFormatting<std::string>> var_fmt_str = var_str;
std::cout << var_int << " " << var_str << std::endl;
std::cout << var_fmt_int << " " << var_fmt_str << std::endl;
但boost::variant<MyFormatting<int>, MyFormatting<std::string>>
法术太久了。我们可以缩短它。
template <class... T>
using Variant_Formatting_t = boost::variant < MyFormatting<T>... > ;
std::cout << Variant_Formatting_t<int, std::string>{var_int} << " " << Variant_Formatting_t<int, std::string>{var_str} << std::endl;
由于boost::variant
使用宏模板元编程来模拟可变参数模板而不是使用C ++ 11可变参数模板,因此我无法使用类型推导使其更清晰。这是我能够达到的最远。