运算符== 如果我包含<iostream>,则不编译

operator== does not compile if I include <iostream>

本文关键字:编译 gt lt 如果 包含 运算符 iostream      更新时间:2023-10-16

以下代码在以下情况下可以完美编译:

  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{}));
~~~~~~^~~~~~~

我在这里做了一个更小的测试用例: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==将签入全局命名空间(始终)、alp(alp::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,转换运算符(To 无效类型)将用于尝试将其与函数参数匹配(如果它们尚未匹配)。显然,这将最终实例化无效类型,因此它将抛出编译时错误。
  • 我的猜测是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 语句)。