如果包含<iostream>,则无法编译operator ==

时间:2019-03-25 19:02:23

标签: c++ iostream

如果满足以下条件,则以下代码可以完美编译:

  1. 我不包括<iostream>

  2. 我将operator==命名为alp::operator==

我想<iostream>operator==有问题,但是我不知道是什么。

我用gcc 7.3.0,clang ++-6.0和goldbolt编译代码。总是相同的错误。

问题是编译器试图将operator==的参数强制转换为const_iterator,但是为什么呢? (我想编译器看不到我的operator==版本,而是在寻找其他版本。)

#include <vector>
#include <iostream> // comment and compile


namespace alp{

template <typename It_base>
struct Iterator {
    using const_iterator    = Iterator<typename It_base::const_iterator>;

    operator const_iterator() { return const_iterator{}; }
};


template <typename It_base>
bool operator==(const Iterator<It_base>& x, const Iterator<It_base>& y)
{ return true;}

}// namespace

struct Func{
    int& operator()(int& p) const {return p;}
};


template <typename It, typename View>
struct View_iterator_base{
    using return_type     = decltype(View{}(*It{})); 

    using const_iterator =
              View_iterator_base<std::vector<int>::const_iterator, Func>;
};


using view_it =
    alp::Iterator<View_iterator_base<std::vector<int>::iterator, Func>>;


int main()
{
    view_it p{};
    view_it z{};

    bool x = operator==(z, p); // only compiles if you remove <iostream>
    bool y = alp::operator==(z,p); // always compile
}

错误消息:

yy.cpp: In instantiation of ‘struct View_iterator_base<__gnu_cxx::__normal_iterator<const int*, std::vector<int> >, Func>’:

yy.cpp:9:73:   required from ‘struct    alp::Iterator<View_iterator_base<__gnu_cxx::__normal_iterator<const int*, std::vector<int> >, Func> >’

yy.cpp:44:29:   required from here

yy.cpp:28:42: error: no match for call to ‘(Func) (const int&)’
 using return_type   = decltype(View{}(*It{}));
                                ~~~~~~^~~~~~~
yy.cpp:22:10: note: candidate: int& Func::operator()(int&) const <near match>
 int& operator()(int& p) const {return p;}
      ^~~~~~~~
yy.cpp:22:10: note:   conversion of argument 1 would be ill-formed:
yy.cpp:28:42: error: binding reference of type ‘int&’ to ‘const int’ discards qualifiers
 using return_type   = decltype(View{}(*It{}));
                                ~~~~~~^~~~~~~

1 个答案:

答案 0 :(得分:5)

我在这里做了一个最小的测试用例:https://godbolt.org/z/QQonMG

相关详细信息是:

  • using类型的别名不会实例化模板。例如:

    template<bool b>
    struct fail_if_true {
        static_assert(!b, "template parameter must be false");
    };
    
    using fail_if_used = fail_if_true<true>;
    

    不会导致编译时错误(如果未使用fail_if_used

  • ADL还检查模板参数类。在这种情况下,std::vector<int>::iterator__gnu_cxx::__normal_iterator<const int*, std::vector<int> >, Func>,其模板中包含std::vector<int>。因此,operator==将(总是)签入全局命名空间,alpalp::Iteratoralp中),__gnu_cxxstd

  • 您的View_iterator_base::const_iterator无效。 View_iterator_base::const_interator::result_type被定义为decltype(Func{}(*std::vector<int>::const_iterator{}))std::vector<int>::const_iterator{}将是向量常量迭代器,因此*std::vector<int>::const_iterator{}const int&Func::operator()取一个int&,因此这表示该表达式无效。但是,由于上述原因,如果不使用它不会导致编译时错误。这意味着您的转换运算符的类型无效。
  • 由于您没有将其定义为explicit,因此,如果转换操作符(对于无效类型)尚未匹配,则将使用它们来尝试将其与函数参数进行匹配。显然,这最终将实例化无效类型,因此将引发编译时错误。
  • 我的猜测是iostream包括string,它为字符串定义了std::operator==

下面是一个没有std名称空间的示例:https://godbolt.org/z/-wlAmv

// Avoid including headers for testing without std::
template<class T> struct is_const { static constexpr const bool value = false; } template<class T> struct is_const<const T> { static constexpr const bool value = true; }

namespace with_another_equals {
    struct T {};

    bool operator==(const T&, const T&) {
        return true;
    }
}

namespace ns {
    template<class T>
    struct wrapper {
        using invalid_wrapper = wrapper<typename T::invalid>;
        operator invalid_wrapper() {}
    };

    template<class T>
    bool operator==(const wrapper<T>&, const wrapper<T>&) {
        return true;
    }
}

template<class T>
struct with_invalid {
    static_assert(!is_const<T>::value, "Invalid if const");
    using invalid = with_invalid<const T>;
};

template<class T>
void test() {
    using wrapped = ns::wrapper<with_invalid<T>>;
    wrapped a;
    wrapped b;
    bool x = operator==(a, b);
    bool y = ns::operator==(a, b);
}

template void test<int*>();

// Will compile if this line is commented out
template void test<with_another_equals::T>();

请注意,仅声明operator const_iterator()应该实例化该类型。但这不是因为它在模板中。我的猜测是,在对其进行检查以表明其无法编译之前,它已经进行了优化(由于未使用,所以会在此处进行编译)(甚至没有-Wall -pedantic警告它没有在我的示例中为return语句。

相关问题