I have a project where I print std::filesystem::directory_entry
from directory_iterator. On the other side, I have a completely independent class with overload std::ostream& operator<<
, that has a templated constructor, which initializes a std::variant
member.
#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;
}
Compilation fails:
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 ...
It seems it tries to wrap directory_entry
into Var
, before sending it to cout
, but I am not sure.
Could you please explain what is actually going on and why code is wrong?
I tested around. For the issue, it seems to do no matter what I put into variant
, even a single variant is ill. This one
#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 VarType&) {
return stream;
}
int main() {
std::cout << std::filesystem::directory_entry() << "\n";
return 0;
}
works fine. If I move _value
initialization into c-tor body compilation fails with the same logical error but for operator=
, at least it is consistent. Evidently it works with non-templated c-tor.
If I move the implementation of ostream& operator<<
into a separate unit and defines it as a friend of Var
, compilation passes (it is a kind of suitable workaround, yet it was not supposed operator<<
has access to private of class). However, it fails if I simply separate and do not make friends.
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;
}
That makes me completely lost. Assuming that it tries to invoke Var
c-tor on <<
here should be no difference.
Why such change does matter?
I build with g++8.4 (g++ -std=c++17 main.cpp var.cpp -lstdc++fs
, also I tried clang7.0 with a similar result).