如何实现<< C ++中的接口?

时间:2015-07-13 05:32:59

标签: c++ class boost

我正在研究一个小型编译器,我使用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); }; 或任何其他内置类型的每个运算符。

任何帮助都将不胜感激。

感谢。

3 个答案:

答案 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可变参数模板,因此我无法使用类型推导使其更清晰。这是我能够达到的最远。