繁体   English   中英

重载分辨率的问题和操作符<<对于模板类型 - 第2部分

[英]problems with overload resolution and operator<< for templated types - part 2

给出以下代码:

#include <string>
#include <type_traits>
#include <sstream>
#include <vector>
#include <iostream>
using namespace std;

namespace has_insertion_operator_impl {
    typedef char no;
    typedef char yes[2];

    struct any_t {
        template <typename T>
        any_t(T const&);
    };

    no operator<<(ostream const&, any_t const&);

    yes& test(ostream&);
    no   test(no);

    template <typename T>
    struct has_insertion_operator {
        static ostream& s;
        static T const&      t;
        static bool const    value = sizeof(test(s << t)) == sizeof(yes);
    };
}

template <typename T>
struct has_insertion_operator : has_insertion_operator_impl::has_insertion_operator<T> {};

template <class T>
typename enable_if<has_insertion_operator<T>::value, string>::type stringify(const T& in) {
    stringstream stream;
    stream << in;
    return stream.str();
}

template <class T>
typename enable_if< ! has_insertion_operator<T>::value, string>::type stringify(const T&) {
    return "{?}";
}

// ======= OVERLOADS PROVIDED BY THE USER =======

template<typename T, typename T2>
struct myType { T data; T2 op; };

template<typename T, typename T2>
ostream& operator<<(ostream& s, const myType<T, T2>&) { s << "myType"; return s; }

template<typename T>
ostream& operator<<(ostream& s, const vector<T>&) { s << "vector<T>"; return s; }

template<typename T, typename A>
ostream& operator<<(ostream& s, const vector<T, A>&) { s << "vector<T, A>"; return s; }

int main() {
    myType<int, float> a;   cout << stringify(a) << endl; // prints "myType"
                            cout << stringify(6) << endl; // prints "6"
    vector<int> v(5);       cout << stringify(v) << endl; // prints "{?}"

    return 0;
}

为什么模板myType<>得到字符串化但是模板化的vector<>类型没有?

对于vector<>类型,我得到默认的{?}字符串,但我显然希望调用底部的一个重载 - 就像myType<>

编辑:

这里的实际问题是为什么has_insertion_operator<vector<int>> false?

我在C ++ 98中也需要它

并且应该在stringify()之后提供operator<< overloads - 就像myType<>

这里有两个问题。

首先是has_insertion_operator<>谓词中的has_insertion_operator<>是错误的。

我换了这个...

template<class T>
struct has_insertion_operator
{
    template<class U>
    static auto test(U*p) -> decltype((std::declval<std::ostream>() << (*p)), void(), std::true_type());

    template<class U>
    static auto test(...) -> std::false_type;

    static constexpr bool value = decltype(test<T>(0))::value;
};

...解决了这个问题,突出了下一个问题(对你来说可能更严重):

./stringify.cpp:73:12: error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup
    stream << in;
           ^
./stringify.cpp:100:37: note: in instantiation of function template specialization 'stringify<std::__1::vector<int, std::__1::allocator<int> > >' requested here
    vector<int> v(5);       cout << stringify(v) << endl; // prints "{?}"
                                    ^
./stringify.cpp:91:10: note: 'operator<<' should be declared prior to the call site
ostream& operator<<(ostream& s, const vector<T>&) { s << "vector<T>"; return s; }
         ^
1 error generated.

这是因为模板函数operator<< <std::vector...>在定义之前被引用。

一旦你把它的定义移到了stringify的定义之上,一切都有效。

最后,请注意:

重载operator<< for std::vector是一个非常糟糕的主意。 它会在以后引起各种令人头疼的问题。

但我正在使用c ++ 98,我希望用户能够提供自己的重载[specializations]

那么,让我们以简单(也是更正确)的方式做到这一点,这将适用于所有类型的c ++,并且由于非法重载侵入std命名空间而不会引起麻烦:

#include <string>
#include <sstream>
#include <vector>
#include <iostream>


// define a template class emitter which by default simply calls operator<<
template<class T>
struct emitter
{
    emitter(const T& v) : _v(v) {}
    std::ostream& operator()(std::ostream& os) const {
        return os << _v;
    }
    const T& _v;
};

// emitter<>'s are streamable
template<class T>
std::ostream& operator<<(std::ostream& os, const emitter<T>& e)
{
    return e(os);
}

// a factory function to generate the correct emitter
template<class T>
emitter<T> emit(const T& v)
{
    return emitter<T>(v);
}

// write one version of stringify in terms of emit<>()
template <class T>
std::string stringify(const T& in) {
    std::stringstream stream;
    stream << emit(in);
    return stream.str();
}
// ======= OVERLOADS PROVIDED BY THE USER =======

template<typename T, typename T2>
struct myType { T data; T2 op; };

// user must provide an emitter for each custom type
template<typename T, typename T2>
struct emitter<myType<T, T2> >
{
    typedef myType<T, T2> value_type;
    emitter(const value_type& v) : _v(v) {}

    std::ostream& operator()(std::ostream& os) const
    {
        return os << "myType";
    }
private:
    const value_type& _v;
};

// and for any std:: templates he wants to support
template<class V, class A>
struct emitter<std::vector<V, A> >
{
    typedef std::vector<V, A> value_type;
    emitter(const value_type& v) : _v(v) {}

    std::ostream& operator()(std::ostream& os) const
    {
        return os << "vector<T, A>";
    }
private:
    const value_type& _v;
};

// test
int main() {
    myType<int, float> a;  std::cout << stringify(a) << std::endl; // prints "myType"
                           std::cout << stringify(6) << std::endl; // prints "6"
    std::vector<int> v(5); std::cout << stringify(v) << std::endl; // prints "vector<T, A>"

    return 0;
}

我认为问题在于查找,我将提供我的理解。

当你在你的has... struct参数依赖查找中调用operator<<并且因为myType位于相同的命名空间中时,重载的operator<<它成立并且你得到一个正确的字符串。 但是当你尝试输出一个vector它试图通过相同的依赖于参数的查找规则来搜索重载的operator<<并且由于std命名空间中没有重载的运算符而无法执行它。 所以它回退到从调用命名空间开始的非限定搜索,因此它找到你的存根operator<<

因此,为了修复它,您可能已将operator<<重载放置到std命名空间(标准禁止)或删除命名空间 - 它将产生相同的效果。

但是,您不必将所有内容都放在私有命名空间之外。 在全局命名空间中执行类似的操作就足够了:

typedef char no;
typedef char yes[2];
template<typename T>
no operator<<(ostream const&, T const&);

或者,如果可能,最好要求库用户将其重载放入其类所在的相同名称空间中。 但它不适用于std成员。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM