如何解决与重载的ostream运算符<<的directory_entry的输出冲突,对于具有变体的类
我有一个项目,我从directory_iterator打印std::filesystem::directory_entry
。另一方面,我有一个完全独立的类,具有重载std::ostream& operator<<
,该类具有模板化构造函数,该构造函数初始化了std::variant
成员。
#include <variant>
#include <iostream>
#include <filesystem>
typedef std::variant<long,std::string> VarType;
class Var {
VarType _value;
public:
template<typename T>
Var(T value) : _value{value} {
}
};
std::ostream& operator<< (std::ostream& stream,const Var&) {
return stream;
}
int main() {
std::cout << std::filesystem::directory_entry() << "\n";//tigger compling error
return 0;
}
编译失败:
main.cpp: In instantiation of ‘Var::Var(T) [with T =
std::filesystem::__cxx11::directory_entry]’: main.cpp:25:49:
required from here main.cpp:11:30: error: no matching function for
call to ‘std::variant<long int,double,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char> > >::variant(<brace-enclosed initializer list>)’
Var(T value) : _value{value} {
... several pages of output ...
在将directory_entry
发送到Var
之前,它似乎尝试将cout
包装到variant
中,但是我不确定。
能否请您解释实际发生的情况以及代码为什么出错?
我在周围测试。对于这个问题,无论我输入什么#include <variant>
#include <iostream>
#include <filesystem>
typedef std::variant<long,const VarType&) {
return stream;
}
int main() {
std::cout << std::filesystem::directory_entry() << "\n";
return 0;
}
,似乎都没有关系,即使只有一个变体也很糟糕。这个
_value
工作正常。如果将operator=
移到c-tor主体中,则初始化会因相同的逻辑错误而失败,但对于ostream& operator<<
,至少是一致的。显然,它适用于非模板c-tor。
如果我将Var
的实现移到一个单独的单元中并将其定义为operator<<
的朋友,则编译会通过(这是一种适当的解决方法,但不应认为{{1 }}可以访问私有课程。但是,如果我只是分开而不交朋友,它就会失败。
main.cpp:
#include "var.hpp"
#include <iostream>
#include <filesystem>
int main() {
std::cout << std::filesystem::directory_entry() << "\n";
std::cout << Var(1l) << "\n";
return 0;
}
var.hpp:
#include <variant>
#include <ostream>
typedef std::variant<long,std::string> VarType;
class Var {
VarType _value;
public:
template<typename T>
Var(T value) : _value{value} {
}
friend std::ostream& operator<< (std::ostream& stream,const Var&); //works
};
//std::ostream& operator<< (std::ostream& stream,const Var&); //instead above does not works
var.cpp:
#include "var.hpp"
std::ostream& operator<< (std::ostream& stream,const Var&) {
return stream;
}
那使我完全迷路了。假设它尝试在Var
上调用<<
c-tor,应该没有什么区别。
为什么这样的变化很重要?
我使用g ++ 8.4(g++ -std=c++17 main.cpp var.cpp -lstdc++fs
进行构建,也尝试了clang7.0并获得类似结果)。
解决方法
在曼苏尔的提示下,我想我发现了。
C-tor
此代码很危险。
class Var {
public:
template<typename T>
Var(T value) {
}
};
避免这种情况。
编译器将尝试在Var
是可见的并且可能合适的任何隐式转换中替换此类c-tor。
将c-tor标记为explicit
是限制这种野蛮替换的最直接方法。 SFINAE和enable_if
可以是限制替换的其他方式。
在我的案例中,不良替代正是打破复杂性的原因,因为在旧版本中,directory_entry
没有ostream<<
的直接定义。编译器寻找转换器,并找到合适的Var
。可以实例化但不能遵从。后者很好,因为如果可以的话,错误是无法追踪的。
有一个有关{2018的补丁程序https://gcc.gnu.org/legacy-ml/gcc-patches/2018-06/msg01084.html(或者可能很快就修复了),引入了明确的ostream << directory_entry
。
在此之前,directory_entry
可以通过隐式c-tor转换为path
template<typename _Source,typename _Require = _Path<_Source>>
path(_Source const& __source,format = auto_format)
这就是如果未明确定义ostream << directory_entry
的情况下ostream<<
起作用的原因。
朋友
Friend实际上限制了转换器的可见性。那也可以
class Var {
VarType _value;
public:
template<typename T>
Var(T value) : _value{value} {
}
friend std::ostream& operator<< (std::ostream& stream,const Var&) {
return stream;
}
};
但是如果将std::ostream&
声明为好朋友,并且ostream << directory_entry
的定义(或其他声明)是可见的,则_value{value}
会再次破坏复杂性,因为其他声明对于野生替代可见。这就解释了为什么将其分成几个单元并使用朋友创建一个解决方法。
SFINAE
SFINAE不检查功能主体。它仅适用于声明。 template< class T,typename = std::enable_if_t<std::disjunction_v<std::is_same<T,long>,std::is_same<T,std::string>>>>
Var(T value) : _value{value} {
}
是身体。调用SFINAE c-tor应该像
std::variant
如何处理import cv2
image = cv2.imread("squirrel.jpg")
cv2.imshow("squirrel",image)
cv2.waitKey(0)
cv2.destroyAllWindows()```
依赖的c-tor的绝妙想法可以在这里找到:
How do I check if an std::variant can hold a certain type。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。