tartanllama / expected Goto Github PK
View Code? Open in Web Editor NEWC++11/14/17 std::expected with functional-style extensions
Home Page: https://tl.tartanllama.xyz
License: Creative Commons Zero v1.0 Universal
C++11/14/17 std::expected with functional-style extensions
Home Page: https://tl.tartanllama.xyz
License: Creative Commons Zero v1.0 Universal
First of all, thanks for sharing a great implementation an enhanced std::expected!
I ran into a problem when trying to use a type that does not have a constexpr constructor with tl::expected<T, E>
when using .and_then()
. Here's a minimal example that reproduces the problem:
#include "expected.hpp"
#include <string>
tl::expected<int, int> operation1() { return 42; }
tl::expected<std::string, int> operation2(int const val) { return "Bananas"; }
tl::expected<std::string, int> do_something()
{
auto const intermediate_result = operation1();
// DOES NOT COMPILE
return intermediate_result.and_then(operation2);
// Must do this instead
//if (!intermediate_result)
// return tl::make_unexpected(intermediate_result.error());
//return operation2(intermediate_result.value());
}
As noted, the line "does not compile" causes the compiler error "error C2476: 'constexpr' constructor does not initialize all members" with Visual Studio 2015 and 2017. Two workarounds that solve the issue are: 1) Remove constexpr
from the expected_storage_base<T, E, false, true>
constructor on line 426 of expected.hpp (not ideal), or 2) add char dummy;
as the first member of the union in that class and then initialize dummy
on line 426. This constructor does not explicitly initialize either union member so the compiler will default initialize the first union member; however, since this constructor is marked constexpr, the first union member must also have a constexpr constructor. The first workaround removes constexpr from the constructor so the the first union member is not required to also have a constexpr constructor, while the second workaround adds another member to the union with a constexpr constructor (default initialization for char
). Initializing the second member of the union instead of the first is not an option since tl::unexpected<E>()
is deleted.
I think a correct solution would be to use something like std::aligned_storage<sizeof(T), alignof(T)>::type
in the union instead of T
directly.
auto return_derived_type() -> expected<std::unique_ptr<derived_type>, std::error_code>
{
....
}
auto test() -> expected<std::unique_ptr<basetype>, std::error_code>
{
// NO VIABLE OVERLOAD '='
expected<std::unique_ptr<basetype>, std::error_code> res = do_something();
...
return res;
}
Isn't there a way to convert an expected taking a derived-type unique_ptr to the basetype version?
Hi!
More as a nice thing to have than anything, would it be possible somehow to write :
tl::optional<T> opt = my_function(...).value_or(tl::nullopt);
assuming my_function
returns a tl::expected<T, E>
? I tried some different syntax but had to fall back to a more classical if
(which is fine).
GCC 8.2 (uses concepts, can't try elsewhere)
Ubuntu 18.04 (WSL)
/usr/bin/g++-8 \
-I../projects/ranges/include \
-I../projects/expected \
-I../../../include \
-isystem ${HOME}/.conan/data/range-v3/1.0.0/cjdb/stable/package/108b10878ddfbada81a0ed2fb115999aafcd316c/include \
-g \
-Wall \
-Wextra \
-Wno-attributes \
-Wsign-promo \
-Woverloaded-virtual \
-Wnon-virtual-dtor \
-Wodr \
-Werror \
-DRANGES_DEEP_STL_INTEGRATION=1 \
-Wcast-align \
-Wconversion \
-Wduplicated-cond \
-Wduplicated-branches \
-Wdouble-promotion \
-Wlogical-op \
-Wnull-dereference \
-Wold-style-cast \
-Wpedantic \
-Wshadow \
-Wsign-conversion \
-Wmisleading-indentation \
-Wunused \
-Wuseless-cast \
-Wformat=2 \
-fstack-protector-all \
-fdiagnostics-color=always \
-fconcepts \
-std=c++2a \
-MD -MT \
source/lexer/CMakeFiles/source.lexer.scan_whitespace.dir/scan_whitespace.cpp.o \
-MF \
source/lexer/CMakeFiles/source.lexer.scan_whitespace.dir/scan_whitespace.cpp.o.d \
-o source/lexer/CMakeFiles/source.lexer.scan_whitespace.dir/scan_whitespace.cpp.o \
-c ../../../source/lexer/scan_whitespace.cpp
../expected/tl/expected.hpp:1771:52: error: exception specification of ‘void tl::expected<T, E>::swap(tl::expected<T, E>&) [with T = ltcpp::source_coordinate; E = std::variant<ltcpp::detail_lexer::not_comment, ltcpp::detail_lexer::unterminated_comment_error>]’ depends on itself
std::is_nothrow_move_constructible<T>::value &&noexcept(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
swap(std::declval<T &>(), std::declval<T &>())) &&
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
std::is_nothrow_move_constructible<E>::value &&
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
noexcept(swap(std::declval<E &>(), std::declval<E &>()))) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../expected/tl/expected.hpp:1770:15: error: ‘swap’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
swap(std::declval<T &>(), std::declval<T &>())) &&
~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../expected/tl/expected.hpp:1770:15: note: declarations in dependent base ‘tl::expected<ltcpp::source_coordinate, std::variant<ltcpp::detail_lexer::not_comment, ltcpp::detail_lexer::unterminated_comment_error> >’ are not found by unqualified lookup
../expected/tl/expected.hpp:1770:15: note: use ‘expected::swap’ instead
../expected/tl/expected.hpp:1770:15: error: no matching function for call to ‘tl::expected<ltcpp::source_coordinate, std::variant<ltcpp::detail_lexer::not_comment, ltcpp::detail_lexer::unterminated_comment_error> >::swap(ltcpp::source_coordinate&, ltcpp::source_coordinate&)’
../expected/tl/expected.hpp:1768:8: note: candidate: ‘void tl::expected<T, E>::swap(tl::expected<T, E>&) [with T = ltcpp::source_coordinate; E = std::variant<ltcpp::detail_lexer::not_comment, ltcpp::detail_lexer::unterminated_comment_error>]’
void swap(expected &rhs) noexcept(
^~~~
../expected/tl/expected.hpp:1768:8: note: candidate expects 1 argument, 2 provided
ninja: build stopped: subcommand failed.
$ git clone https://github.com/TartanLlama/expected.git && echo '#include <tl/expected.hpp>' > test.cpp && clang++-9 -c -I./expected -std=c++11 test.cpp
In file included from test.cpp:1:
./expected/tl/expected.hpp:768:18: warning: 'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const' to avoid a change in behavior
[-Wconstexpr-not-const]
constexpr void destroy_val() {
^
const
./expected/tl/expected.hpp:768:18: error: constexpr function's return type 'void' is not a literal type
./expected/tl/expected.hpp:823:18: warning: 'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const' to avoid a change in behavior
[-Wconstexpr-not-const]
constexpr void destroy_val() {
^
const
./expected/tl/expected.hpp:823:18: error: constexpr function's return type 'void' is not a literal type
2 warnings and 2 errors generated.
A fairly serious bug:
#include <memory>
#include <system_error>
#include <iostream>
using namespace tl;
struct parent {};
struct child : public parent {};
auto doit() -> expected<std::unique_ptr<child>, std::error_code>
{
return make_unexpected(std::error_code{});
}
int main ()
{
// Works
expected<std::unique_ptr<child>, std::error_code> res1 = doit();
if (res1) std::cout << "All OK\n"; else std::cout << "An error!" << std::endl;
// Upcast removes the error!
expected<std::unique_ptr<parent>, std::error_code> res2 = doit();
if (res2) std::cout << "All OK\n"; else std::cout << "An error!" << std::endl;
return 0;
}
Output:
An error!
All OK
Expected output:
An error!
An error!
struct S {
int x;
};
struct F {
int x;
};
tl::expected<S, F> f(bool succeed) {
if(succeed) {
return S{42};
}
return tl::expected<S, F>{tl::unexpect, F{1337}};
}
template<typename>
struct inspect;
int main() {
auto res = f(true);
res.map([](S s) {
printf("%d", s.x);
});
res.map_error([](auto r) {
if constexpr(std::is_same_v<F, decltype(r)>) {
printf("%d", r.x);
}
else {
inspect<decltype(f)>{};
}
});
This should compile
Hi. The links on this documentation page all seem to return a 404 error except for links to std
-namespace definitions which don't fare much better.
Even after #8, I see a lot of warnings under Clang (using -W -Wall
):
./tl/expected.hpp:690:30: warning: unused parameter 'rhs' [-Wunused-parameter]
void construct_with(Rhs && rhs) noexcept {
^
./tl/expected.hpp:2060:48: warning: unused parameter 'x' [-Wunused-parameter]
constexpr bool operator<(const expected<T, E> &x, const unexpected<E> &e) {
^
./tl/expected.hpp:2060:72: warning: unused parameter 'e' [-Wunused-parameter]
constexpr bool operator<(const expected<T, E> &x, const unexpected<E> &e) {
^
./tl/expected.hpp:2064:47: warning: unused parameter 'e' [-Wunused-parameter]
constexpr bool operator<(const unexpected<E> &e, const expected<T, E> &x) {
^
./tl/expected.hpp:2068:73: warning: unused parameter 'e' [-Wunused-parameter]
constexpr bool operator<=(const expected<T, E> &x, const unexpected<E> &e) {
^
./tl/expected.hpp:2072:48: warning: unused parameter 'e' [-Wunused-parameter]
constexpr bool operator<=(const unexpected<E> &e, const expected<T, E> &x) {
^
./tl/expected.hpp:2072:73: warning: unused parameter 'x' [-Wunused-parameter]
constexpr bool operator<=(const unexpected<E> &e, const expected<T, E> &x) {
^
./tl/expected.hpp:2076:72: warning: unused parameter 'e' [-Wunused-parameter]
constexpr bool operator>(const expected<T, E> &x, const unexpected<E> &e) {
^
./tl/expected.hpp:2080:47: warning: unused parameter 'e' [-Wunused-parameter]
constexpr bool operator>(const unexpected<E> &e, const expected<T, E> &x) {
^
./tl/expected.hpp:2080:72: warning: unused parameter 'x' [-Wunused-parameter]
constexpr bool operator>(const unexpected<E> &e, const expected<T, E> &x) {
^
./tl/expected.hpp:2084:49: warning: unused parameter 'x' [-Wunused-parameter]
constexpr bool operator>=(const expected<T, E> &x, const unexpected<E> &e) {
^
./tl/expected.hpp:2084:73: warning: unused parameter 'e' [-Wunused-parameter]
constexpr bool operator>=(const expected<T, E> &x, const unexpected<E> &e) {
^
./tl/expected.hpp:2088:48: warning: unused parameter 'e' [-Wunused-parameter]
constexpr bool operator>=(const unexpected<E> &e, const expected<T, E> &x) {
^
The simplest fix is, of course, just remove the unused parameter names. But it only makes sense for the first warning (I misunderstood when I first reported the issue: now edited). The key problem is:
I have two alternatives:
ErrorExists
error, and make the return type expected<bool, ErrorExists>
. A boolean return value is used when both arguments have values. When at least one argument does not have a valid value, ErrorExists
is returned instead of a fake boolean value. Comparing order of errors seems useless to me: think about comparing two errno
s, or two exception_ptr
s.What do you think?
tl::expected< void, std::string > voidtest() {
return {};
}
int main(int argc, char *argv[])
{
voidtest()
.and_then( []() {} );
}
it's compile error ocurr
( win10, mingw 5.3 )
src\main.cpp:28: error: no matching function for call to 'tl::expected<void, std::__cxx11::basic_string<char> >::and_then(main(int, char**)::<lambda()>)'
.and_then( []() {} );
^
I'm submitting my Conan recipe for tl::expected
to conan-center-index: conan-io/conan-center-index#556. If accepted, this will deprecate https://github.com/yipdw/conan-tl-expected and the name of the package will be tl-expected/x.y.z
(no user/channel
part required).
conan-center-index has a policy that only official releases of a package can be included in the index, and tl::expected
1.0.1 fixes a bug that I've hit in the past. Can a release including that bugfix be made? If there are blockers, is there anything I can do to help?
I'm guessing that this is maybe controversial in the wider community, but I'm looking into adopting tl::expected
and I'm not quite sure why it's not [[nodiscard]], is there a reason for this?
It seems like this implementation of expected
is unable to select the move constructor on GCC >= 5 for types that are more complex. While this compiles without issue:
#include <expected.hpp>
struct Foo {
Foo() {}
Foo(Foo&) = delete;
Foo(Foo&&) {}
};
tl::expected<Foo, std::exception_ptr> bar() {
Foo foo;
return { std::move(foo) };
}
adding std::vector
(which has move constructors as well) into the mix will make it fail on GCC >= 5. It compiles fine on Clang, and GCC 4.9.
#include <expected.hpp>
#include <vector>
struct Foo {
Foo(Foo&) = delete;
Foo(Foo&&) {}
};
tl::expected<std::vector<Foo>, std::exception_ptr> bar() {
std::vector<Foo> foos;
return { std::move(foos) };
}
The MSVC preview doesn't seem to like the and_then implementation
Happened with /std:c++latest /permissive-. I haven't tested to see if it works with flags other than that.
src\common/expected.hpp(1227): error C2355: 'this': can only be referenced inside non-static member functions or non-static data member initializers
src\common/expected.hpp(1895): note: see reference to class template instantiation 'tl::expected<T,E>' being compiled
src\common/expected.hpp(1235): error C2355: 'this': can only be referenced inside non-static member functions or non-static data member initializers
src\common/expected.hpp(1243): error C2355: 'this': can only be referenced inside non-static member functions or non-static data member initializers
src\common/expected.hpp(1252): error C2355: 'this': can only be referenced inside non-static member functions or non-static data member initializers
My LLVM complains when compiling expected::operator->() const
.
I've had to add:
const T *valptr() const { return std::addressof(this->m_val); }
expected/include/tl/expected.hpp
Line 187 in fd96e45
i get an annoying warning when compiling with -fno-exceptions, in my local copy i solved it by adding after line 190 (void) e;
There have been a few versions of the specification released since I wrote this. I should see if it's worth updating for parity.
I believe that using tl::expected
with a class X
that contains const member variables is undefined behaviour. The same can actually be said for some STL containers, such as std::vector
! This is due to assumptions made by the compiler about the value held at the address of a const member variable. The paper on std::launder gives some really good examples about this (it even uses optional as an example), so I'll let it do the explaining.
It more or less comes down to the usage of placement new
and accessing the object at that address. Whenever the object (at the address passed to placement new
) is accessed, it needs to be accessed through the pointer returned by std::launder
. As an example, when the destructor gets invoked here:
~expected_storage_base() {
if (m_has_val) {
m_val.~T();
} else {
m_unexpect.~unexpected<E>();
}
}
It's my understanding that this would need to be replaced with:
std::launder(&m_val)->~T();
The same applies for tl::optional, I believe :) thank you for the great work you've done on both of these!
Is the following snipped the idiomatic way or is there some helper or shortcut that can be used to avoid writing out the full type?
What is the performance implication of returning a void expected object compared to an option or no error wrapper?
tl::expected<void, std::string> void_func()
{
return tl::expected<void, std::string>();
}
Hi,
The minimal example to reproduce it is:
void test() {
expected<void, int> res = {};
res.map_error([](auto i) { return i; });
}
I think there is something wrong with the expect<void>
specialization?
Is the line
template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
std::declval<Exp>().error())),
detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
auto map_error_impl(Exp &&exp, F &&f) {
using result = expected<exp_t<Exp>, monostate>;
if (exp.has_value()) {
return result(*std::forward<Exp>(exp)); <------------------- this
}
detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
return result(unexpect, monostate{});
}
still available when exp is void?
Currently, I have to use map([](auto v) {return 0;})
to workaround this.
You can find it here:
https://gcc.godbolt.org/z/EFLUdC
Also, I found I can't compile it with GCC 8-.
https://github.com/TartanLlama/expected/blob/master/tl/expected.hpp#L726
Doesn't compile on Windows (AppVeyor MSVC)
I think this should work, but it doesn't. Assigning an unexpected attempts to destruct the value which doesn't exist in the case of void
.
auto result = tl::expected<void, std::string>{};
result = tl::make_unexpected(std::string{"foo"}); // Fails to compile
Thanks for creating optional and expected! I like them so much I'm using both in the same project.
However, when both headers are included together, three macros get redefined:
IS_TRIVIALLY_COPY_CONSTRUCTIBLE
IS_TRIVIALLY_COPY_ASSIGNABLE
IS_TRIVIALLY_DESTRUCTIBLE
To address this, the macros could be defined to be unique to each project or wrapped with ifndef
.
What do you think? Thanks for considering!
tl::expected< int, std::string > error() {
return tl::make_unexpected( std::string( "error1 " ) );
}
std::string maperror( std::string s ) {
return s + "maperror ";
}
int main(int argc, char *argv[])
{
auto result = error()
.map_error( maperror ) ;
if( result.has_value() )
std::cout << "result: " << result.value();
else
std::cout << "result: fail: " << result.error();
}
it's compile error; ( windows 10, mingw 5.3 32bit )
expected\tl\expected.hpp:2022: error: no matching function for call to 'tl::expected<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >::expected(int)'
? result(*std::forward<Exp>(exp))
^
but same type is success. Is this right?
tl::expected< std::string, std::string > error() {
.....
I noticed on the list of supported VC++ compilers VS 2019 was not listed. Is there any reason why this wouldn't work in VS 2019?
error: cannot refer to class template 'unexpected' without a template argument list
return unexpected(err_msg);
I get this when returning the unexpected value on macos 10.12.6 with clang from xcode 9.2 and llvm@6 and llvm@7 from brew. The same code works well in ubuntu and manjaro. Thanks.
Reported via Slack by @nikitablack.
tl::expected<int, string> getInt3(int val)
{
return val;
}
tl::expected<int, string> getInt2(int val)
{
return val;
}
tl::expected<int, string> getInt1()
{
// this works ok
/*auto int2 = getInt2(5);
if (!int2) {
return int2;
}
auto int3 = getInt3(*int2);
if (!int3) {
return int3;
}
return *int3;*/
// this fails to compile
return getInt2(5).and_then(getInt3);
}
error C2672: 'tl::expected<int,std::string>::and_then': no matching overloaded function found
error C2893: Failed to specialize function template 'invoke_result_impl<F,void,T&>::type tl::expected<T,std::string>::and_then(F &&) &'
with
[
T=int
]
note: With the following template arguments:
note: 'F=tl::expected<int,std::string> (__cdecl &)(int)'
Is tl::expected
meant to be trivial if both template types are trivial? The below snippet demonstrates this:
// Trivial
static_assert(std::is_trivial<VW::vw_error>::value, "");
// Not trivial
static_assert(!std::is_trivial<tl::expected<int, VW::vw_error>>::value, "");
static_assert(!std::is_trivial<tl::expected<void, VW::vw_error>>::value, "");
I would really like to use your library because I think it's the only one to claim c++11 support (I have to deal with this requirement) and the functional extensions.
Unfortunately it doesn't compile:
git clone https://github.com/TartanLlama/expected.git
echo '#include <tl/expected.hpp>' > test.cpp
g++ -c -I./expected -std=c++11 test.cpp
This result in the following output:
In file included from test.cpp:1:0:
./expected/tl/expected.hpp:123:31: fout: ‘constexpr E& tl::unexpected<E>::value() const &’ cannot be overloaded
TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; }
^
./expected/tl/expected.hpp:121:22: fout: with ‘constexpr const E& tl::unexpected<E>::value() const &’
constexpr const E &value() const & { return m_val; }
^
./expected/tl/expected.hpp:127:23: fout: ‘constexpr const E&& tl::unexpected<E>::value() const &&’ cannot be overloaded
constexpr const E &&value() const && { return std::move(m_val); }
^
./expected/tl/expected.hpp:125:32: fout: with ‘constexpr E&& tl::unexpected<E>::value() const &&’
TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); }
^
./expected/tl/expected.hpp:659:22: fout: ‘constexpr const T& tl::detail::expected_operations_base<T, E>::get() const &’ cannot be overloaded
constexpr const T &get() const & { return this->m_val; }
^
./expected/tl/expected.hpp:658:31: fout: with ‘constexpr T& tl::detail::expected_operations_base<T, E>::get() const &’
TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; }
^
./expected/tl/expected.hpp:662:23: fout: ‘constexpr const T&& tl::detail::expected_operations_base<T, E>::get() const &&’ cannot be overloaded
constexpr const T &&get() const && { return std::move(this->m_val); }
^
./expected/tl/expected.hpp:660:32: fout: with ‘constexpr T&& tl::detail::expected_operations_base<T, E>::get() const &&’
TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); }
^
./expected/tl/expected.hpp:668:34: fout: ‘constexpr const tl::unexpected<E>& tl::detail::expected_operations_base<T, E>::geterr() const &’ cannot be overloaded
constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
^
./expected/tl/expected.hpp:665:43: fout: with ‘constexpr tl::unexpected<E>& tl::detail::expected_operations_base<T, E>::geterr() const &’
TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
^
./expected/tl/expected.hpp:673:35: fout: ‘constexpr const tl::unexpected<E>&& tl::detail::expected_operations_base<T, E>::geterr() const &&’ cannot be overloaded
constexpr const unexpected<E> &&geterr() const && {
^
./expected/tl/expected.hpp:669:44: fout: with ‘constexpr tl::unexpected<E>&& tl::detail::expected_operations_base<T, E>::geterr() const &&’
TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
^
./expected/tl/expected.hpp:724:34: fout: ‘constexpr const tl::unexpected<E>& tl::detail::expected_operations_base<void, E>::geterr() const &’ cannot be overloaded
constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
^
./expected/tl/expected.hpp:721:43: fout: with ‘constexpr tl::unexpected<E>& tl::detail::expected_operations_base<void, E>::geterr() const &’
TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
^
./expected/tl/expected.hpp:729:35: fout: ‘constexpr const tl::unexpected<E>&& tl::detail::expected_operations_base<void, E>::geterr() const &&’ cannot be overloaded
constexpr const unexpected<E> &&geterr() const && {
^
./expected/tl/expected.hpp:725:44: fout: with ‘constexpr tl::unexpected<E>&& tl::detail::expected_operations_base<void, E>::geterr() const &&’
TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
^
In file included from test.cpp:1:0:
./expected/tl/expected.hpp:1331:41: fout: ‘template<class T, class E> template<class F> constexpr tl::expected<T, E> tl::expected<T, E>::or_else(F&&) const &’ cannot be overloaded
template <class F> expected constexpr or_else(F &&f) const & {
^
./expected/tl/expected.hpp:1323:56: fout: with ‘template<class T, class E> template<class F> constexpr tl::expected<T, E> tl::expected<T, E>::or_else(F&&) const &’
template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & {
^
./expected/tl/expected.hpp:1335:41: fout: ‘template<class T, class E> template<class F> constexpr tl::expected<T, E> tl::expected<T, E>::or_else(F&&) const &&’ cannot be overloaded
template <class F> expected constexpr or_else(F &&f) const && {
^
./expected/tl/expected.hpp:1327:56: fout: with ‘template<class T, class E> template<class F> constexpr tl::expected<T, E> tl::expected<T, E>::or_else(F&&) const &&’
template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && {
^
./expected/tl/expected.hpp:1677:31: fout: ‘constexpr T* tl::expected<T, E>::operator->() const’ cannot be overloaded
TL_EXPECTED_11_CONSTEXPR T *operator->() { return valptr(); }
^
./expected/tl/expected.hpp:1675:22: fout: with ‘constexpr const T* tl::expected<T, E>::operator->() const’
constexpr const T *operator->() const { return valptr(); }
^
./expected/tl/expected.hpp:1737:31: fout: ‘constexpr E& tl::expected<T, E>::error() const &’ cannot be overloaded
TL_EXPECTED_11_CONSTEXPR E &error() & { return err().value(); }
^
./expected/tl/expected.hpp:1735:22: fout: with ‘constexpr const E& tl::expected<T, E>::error() const &’
constexpr const E &error() const & { return err().value(); }
^
./expected/tl/expected.hpp:1741:32: fout: ‘constexpr E&& tl::expected<T, E>::error() const &&’ cannot be overloaded
TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(err().value()); }
^
./expected/tl/expected.hpp:1739:23: fout: with ‘constexpr const E&& tl::expected<T, E>::error() const &&’
constexpr const E &&error() const && { return std::move(err().value()); }
^
./expected/tl/expected.hpp: In constructor ‘tl::expected<T, E>::expected(const tl::expected<U, G>&)’:
./expected/tl/expected.hpp:1430:3: fout: constexpr constructor does not have empty body
}
^
./expected/tl/expected.hpp: In constructor ‘tl::expected<T, E>::expected(const tl::expected<U, G>&)’:
./expected/tl/expected.hpp:1446:3: fout: constexpr constructor does not have empty body
}
^
./expected/tl/expected.hpp: In constructor ‘tl::expected<T, E>::expected(tl::expected<U, G>&&)’:
./expected/tl/expected.hpp:1460:3: fout: constexpr constructor does not have empty body
}
^
./expected/tl/expected.hpp: In constructor ‘tl::expected<T, E>::expected(tl::expected<U, G>&&)’:
./expected/tl/expected.hpp:1475:3: fout: constexpr constructor does not have empty body
$ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.5' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.5)
The bug is fairly obvious from the source code:
template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
*std::declval<Exp>())),
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
constexpr detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
if (exp.has_value()) {
return std::forward<Exp>(exp);
}
return detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
}
template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
*std::declval<Exp>())),
detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
if (exp.has_value()) {
return std::forward<Exp>(exp);
}
detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
return std::forward<Exp>(exp);
}
The calls to *forward<Exp>(exp)
are guaranteed to return a reference to T
, from a memory area that holds an ´E`, in the (implicit) else branches.
I presume the correct behaviour is to accept a function that takes an E
, and call it with .error()
?
On Ubuntu 18.04 (g++-7) I got a strange compile error for this sequence of code:
tl::expected<std::vector<char>,bool> v1; auto v2 = v1;
The compiler complains, that copying is disabled. The strange thing is, that this error only occurs, when vector
is included before expected.hpp
. No error occurs, when the include order is swapped. I don't know, if this is a compiler bug or in implementation issue. I was able to reproduce the bug on the compiler explorer web-site. It seems, that the bug is present in g++ 5.x - 7.x.
using MaybeDataPtr = tl::expected<int, std::unique_ptr<int>>;
MaybeDataPtr test(int i) noexcept
{
return std::move(i);
}
MaybeDataPtr test2(int i) noexcept
{
return std::move(i);
}
auto m = test(10)
.and_then(test2);
Used with gcc-7.2.0.
What would it take to build a benchmark comparison as described in Niall Douglas' ACCU 2017 Talk?
As I mentioned last time, I do not think it good to make functions like operator<(const expected& lhs, const expected& rhs)
return a simple bool
, if an error might be present. Some potential alternatives for discussion:
expected<bool, error_exists>
, where error_exists
is a special new type. This is what I proposed last time.std::optional<bool>
. It is similar to the previous proposal, but can be implemented in a simpler way. The drawback is the requirement of a C++17 compiler.error_exists
exception should be thrown when either lhs
or rhs
contains an error.For broad applicability, I do not prefer the middle approach. The first option conforms to the expected
rationale, but makes the client code a bit less straightforward. The last approach allows the most straightforward usage, but it seems a bit weird under the context of expected
.
There is another view angle, though. If we take into account "concepts", which will likely make into C++20, a Regular object should be EqualityComparable, which requires that ==
and !=
can work and result in a type that can be converted to bool
implicitly. Another concept, StrictTotallyOrdered, requires operators like <
, and that the result should be convertible to bool
. Neither of the first two options can satisfy these requirements, which would make the expected
type Semiregular at best. For future safety, I think comparison operators involving expected
objects should return bool
but may throw. We may provide alternative functions (not operators) like tl::eq
and tl::gte
, which return an expected
object and will not throw (if value comparison does not throw).
What do you think?
Example: https://gcc.godbolt.org/z/wkrSFR
Should an r-value ref qualified map
move T when invoking f?
This fails to compile with the following error that indicates that the deleted copy constructor is used.
tl::expected<int, std::unique_ptr<std::string>> func()
{
return 1;
}
/mnt/c/w/repos/perf_tests/perf_tests/../sub/expected/include/tl/expected.hpp: In instantiation of ‘U& tl::expected<T, E>::value() & [with U = int; tl::detail::enable_if_t<(! std::is_void<_Res>::value)>* <anonymous> = 0u; T = int; E = std::unique_ptr<std::__cxx11::basic_string<char> >]’:
/mnt/c/w/repos/perf_tests/perf_tests/expected.cpp:161:37: required from here
/mnt/c/w/repos/perf_tests/perf_tests/../sub/expected/include/tl/expected.hpp:1903:30: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = std::__cxx11::basic_string<char>; _Dp = std::default_delete<std::__cxx11::basic_string<char> >]’
detail::throw_exception(bad_expected_access<E>(err().value()));
^
In file included from /usr/include/c++/5/memory:81:0,
from /mnt/c/w/repos/perf_tests/perf_tests/expected.cpp:2:
/usr/include/c++/5/bits/unique_ptr.h:359:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
^
In file included from /mnt/c/w/repos/perf_tests/perf_tests/expected.cpp:5:0:
/mnt/c/w/repos/perf_tests/perf_tests/../sub/expected/include/tl/expected.hpp:1191:12: note: initializing argument 1 of ‘tl::bad_expected_access<E>::bad_expected_access(E) [with E = std::unique_ptr<std::__cxx11::basic_string<char> >]’
explicit bad_expected_access(E e) : m_val(std::move(e)) {}
Compiler:
g++ (Ubuntu 5.5.0-12ubuntu1~16.04) 5.5.0 20171010
Hi,
I have a function that returns tl::expected<int, std::exception_ptr>
and is marked as noexcept
. Inside that function I try to catch any exception that might be in flight and wrap it in tl::expected
by calling tl::make_unexpected(std::current_exception())
. That, unsurprisingly, compiles just fine. If I then attempt to run the code analysis (VS 2019 16.3) on said function, I get the following warning: C26447: The function is declared 'noexcept' but calls function 'make_unexpected<std::exception_ptr>()' which may throw exceptions (f.6).
Obviously, I can suppress this warning, but I'd like to know, if there's a way to call make_unexpected
inside a noexcept function, that avoids triggering this particular warning? Other than modifying expected.hpp
and making th make_unexpected
fn and unexpected
ctors conditionally noexcept (if the error type is noexcept copy-constructible or noexcept move-constructible)?
Hi, I am trying to use this library but I am having some compilations errors. I tested on gcc 4.8, gcc 5.4, gcc 5.5 and gcc 6.4 using the gcc docker image for each version. I was able to compile the following source code only on gcc 4.8.
The source file:
#include "expected.hpp"
int main() {}
Build Command:
g++ source.cpp -std=c++11
Compilation Errors:
expected.hpp:94:3: error: 'is_trivially_copy_constructible' is not a member of 'tl::detail'
tl::detail::is_trivially_copy_constructible
^
expected.hpp:814:32: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>::
^
expected.hpp:94:3: note: suggested alternative:
tl::detail::is_trivially_copy_constructible
^
expected.hpp:814:32: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>::
^
In file included from service.cpp:31:0:
/usr/local/include/c++/5.4.0/type_traits:1336:12: note: 'std::is_trivially_copy_constructible'
struct is_trivially_copy_constructible
^
In file included from service.cpp:32:0:
expected.hpp:94:3: error: 'is_trivially_copy_constructible' is not a member of 'tl::detail'
tl::detail::is_trivially_copy_constructible
^
expected.hpp:814:32: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>::
^
expected.hpp:94:3: note: suggested alternative:
tl::detail::is_trivially_copy_constructible
^
expected.hpp:814:32: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>::
^
In file included from service.cpp:31:0:
/usr/local/include/c++/5.4.0/type_traits:1336:12: note: 'std::is_trivially_copy_constructible'
struct is_trivially_copy_constructible
^
In file included from service.cpp:32:0:
expected.hpp:94:48: error: template argument 2 is invalid
tl::detail::is_trivially_copy_constructible
^
expected.hpp:814:32: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>::
^
expected.hpp:815:15: error: 'value' in namespace '::' does not name a type
value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
^
expected.hpp:822:8: error: 'expected_copy_base' is not a class template
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
^
expected.hpp:94:3: error: 'is_trivially_copy_constructible' is not a member of 'tl::detail'
tl::detail::is_trivially_copy_constructible
^
expected.hpp:879:30: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
^
expected.hpp:94:3: note: suggested alternative:
tl::detail::is_trivially_copy_constructible
^
expected.hpp:879:30: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
^
In file included from service.cpp:31:0:
/usr/local/include/c++/5.4.0/type_traits:1336:12: note: 'std::is_trivially_copy_constructible'
struct is_trivially_copy_constructible
^
In file included from service.cpp:32:0:
expected.hpp:94:3: error: 'is_trivially_copy_constructible' is not a member of 'tl::detail'
tl::detail::is_trivially_copy_constructible
^
expected.hpp:879:30: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
^
expected.hpp:94:3: note: suggested alternative:
tl::detail::is_trivially_copy_constructible
^
expected.hpp:879:30: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
^
In file included from service.cpp:31:0:
/usr/local/include/c++/5.4.0/type_traits:1336:12: note: 'std::is_trivially_copy_constructible'
struct is_trivially_copy_constructible
^
In file included from service.cpp:32:0:
expected.hpp:94:48: error: template argument 2 is invalid
tl::detail::is_trivially_copy_constructible
^
expected.hpp:879:30: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
^
expected.hpp:97:55: error: wrong number of template arguments (3, should be 2)
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible
^
expected.hpp:880:30: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE'
TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value
^
expected.hpp:317:76: note: provided for 'template<class T, class U> using is_void_or = tl::detail::conditional_t<std::is_void<_Tp>::value, std::integral_constant<bool, true>, U>'
using is_void_or = conditional_t<std::is_void::value, std::true_type, U>;
^
expected.hpp:880:74: error: 'value' in namespace '::' does not name a type
TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value
^
expected.hpp:889:8: error: 'expected_copy_assign_base' is not a class template
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
^
Apologies in advance for the question-oriented submission. There is no bug, and tl::expected
is awesome. I am trying to figuring out the best way to pass varadic parameters with the and_then()
monad idiom.
For example, from the canonical example:
auto with_tie = add_bow_tie(*cropped);
if (!with_tie) return with_tie;
becomes:
.and_then(add_bow_tie)
So far so good. But in about 100% of my cases, in reality I have:
auto with_tie = add_bow_tie(*cropped, tiecolor::red);
Which I've been addressing with a mess of code that looks like:
auto add_red_bow_tie = [](const image& img) {
return add_bow_tie(img, tiecolor::red);
}
[...]
.and_then(add_red_bow_tie)
What I would really like is something (?) like:
.and_then(add_bow_tie, tiecolor::red)
But before I proceed to specialize tl::expected
, and probably do it wrong, I was hoping someone might have some suggestions on the correct approach. Or alternatively, confirm I should just stick with all the little lambdas or std::bind
. Note I am absolutely not asking nor suggesting tl::expected
should be changed. I am just looking to use it right.
void expected is not available due to the and_then design.
I am using map instead.
However, it can not be used when error handling is required on the map.
So the code gets messy.
tl :: expected <void, std :: string> result = voidwork ();
if (result) {
tl :: expected <int, std :: string> work2result = work2 ();
}
if (! result) {
errorhandling (result.error ())
return result;
}
to
tl :: expected <int, std :: string> result = voidWork ()
.and_then (work2);
.map_error ([&] (std :: string result) {errorhandling (result);
return result;
void expected also requires and_then and or_else.
There are quite a few trailing spaces in expected.hpp. They are quite annoying in my Vim using the 80-column text width.
Since this is so trivial, I am not sure whether you want a PR. If you do want a PR, I can do it.
Hello TL :)
This might be a non-issue, it's likely I've overlooked something, but it would appear that the usage of placement new here:
template <class... Args> void construct(Args &&... args) noexcept {
new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
this->m_has_val = true;
}
template <class Rhs> void construct_with(Rhs &&rhs) noexcept {
new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
this->m_has_val = true;
}
begins to misbehave in some cases when constructing an expected<T const>
from an expected<T>
. std::addressof
returns a pointer of type T const *
which seems to confuse the overload resolution of operator new. E.g.
#include "expected.hpp"
#include <iostream>
class A
{
public:
A() = default;;
A(A const &) = default;
A(A&&) = default;
~A() {}
A& operator=(A const &) = default;
A& operator=(A &&) = default;
int i = 5u;
};
template< typename T>
using Expected = tl::expected<T, int>;
Expected<A> getSomeExpected()
{
return Expected<A>(A{});
}
int main()
{
Expected<A const> a (getSomeExpected());
}
Gives the following:
1> error C2665: 'operator new': none of the 4 overloads could convert all the argument types
...
1> expectedtest\expected.hpp(665): note: while trying to match the argument list '(unsigned __int64, _Ty *)'
1> with
1> [
1> _Ty=const A
1> ]
Casting the address type passed to placement new works, although I suspect that's probably UB.
Compiled with:
MSVC++ 14.16, _MSC_VER == 1916 (Visual Studio 2017 version 15.9)
/std:c++17
Note: I suck at CMake so this might not be a bug but rather a misunderstanding on my part.
For my project, I use CLion on Windows with CMake and git submodules for libraries.
I add libraries to my .gitmodules file and then in my CMakeLists.txt file I do add_subdirectory(libs/expected)
.
In my .gitmodules the library is included as such:
[submodule "libs/expected"]
path = libs/expected
url = https://github.com/TartanLlama/expected
When I sync CMake after including expected
it gives an error:
CMake Error at libs/expected/CMakeLists.txt:35 (add_executable):
add_executable cannot create ALIAS target "tl::expected" because target
"expected" is not an executable.
To fix this error I modify expected\CMakeLists.txt by removing commenting the following lines:
34 | #if (NOT CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
35 | # add_executable(tl::expected ALIAS expected)
36 | #endif()
I have no idea why these lines cause an error and my knowledge of CMake is very limited, but every other library I use works fine by default.
If anyone can provide a solution for this or investigate if it's an actual issue that would be cool.
If there is something that I missed in terms of how to use the library with CMake maybe it would be nice to mention in the readme how to avoid this issue? At the very least I hope if someone else ever deals with this they can find a solution here.
The minimal reproducible case is here: https://godbolt.org/z/qRw6Qi
A slightly different example (the only change is in line 31) might indicate something related to copy constructibility: https://godbolt.org/z/1Q3_qv
Another example (again, only change is in line 31) : https://godbolt.org/z/l9SjU6
The example works with gcc-8.0, and llvm-6.0 and llvm-7.0.
This compiles faster, as pointed out by Odin.
Hey Sy
I was wondering if there will be reference support for tl::expected at some point in the future? I've just checked out the latest draft p0323r8 for std::expected and although I'd already expected ( 😁 sorry ) it std::expected is going to be consistent with std::optional and therefor not allow references.
Your tl::optional implementation on the other hand does allow references and even explicitly says so in the description. Is there any particular reason why you'd want support for one type but not the other?
Best regards
Vincent
How amenable would people be to some kind of "try" functionality? It's described more fully here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0779r0.pdf
I'm not sure about the status of that paper, but coming from Rust try!()
and the ?
operator are extremely useful when dealing with types like expected
.
Obviously we wouldn't be able to have it as a real keyword/operator, but the less-ideal macro version seems pretty straightforward (TRYX()
from the linked paper).
I could of course just send in a PR, but I thought it might be better to discuss it first.
It would be nice to have either a preprocessor option or a separate set of functions (unsafe_value()
?) to disable throws in expected
and invoke undefined behavior instead because this fails to compile with gcc and the -fno-exceptions
flag. This makes it impossible to use in constrained environments like microcontrollers and other freestanding implementations where exceptions are either too expensive or simply not implemented.
This can be implemented in standard C++ with the noreturn
attribute which gcc and clang can take advantage of:
template<typename E>
[[noreturn]] constexpr void throw_exception(E &&e) {
#ifndef DISABLE_EXCEPTIONS
throw std::forward<E>(e);
#endif
}
Hello and thanks for sharing this nice library!
With the merge of the swap
branch the build without exceptions broke.
^
thirdparty/expected/tl/expected.hpp:1889:7: error: cannot use 'throw' with exceptions disabled
throw;
^
thirdparty/expected/tl/expected.hpp:1882:5: error: cannot use 'try' with exceptions disabled
try {
^
thirdparty/expected/tl/expected.hpp:1905:7: error: cannot use 'throw' with exceptions disabled
throw;
^
thirdparty/expected/tl/expected.hpp:1898:5: error: cannot use 'try' with exceptions disabled
try {
GCC 5.5 seems to have a compiler bug which results in errors like this:
<source>:2037:26: error: call of overloaded 'map(main()::<lambda(int)>&)' is ambiguous
auto ret = e.map(mul2);
^
<source>:1163:52: note: candidate: constexpr auto tl::expected<T, E>::map(F&&) & [with F = main()::<lambda(int)>&; T = int; E = int]
template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & {
^
<source>:1169:52: note: candidate: constexpr auto tl::expected<T, E>::map(F&&) && [with F = main()::<lambda(int)>&; T = int; E = int]
template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && {
^
<source>:1175:37: note: candidate: constexpr auto tl::expected<T, E>::map(F&&) const & [with F = main()::<lambda(int)>&; T = int; E = int]
template <class F> constexpr auto map(F &&f) const & {
^
<source>:1181:37: note: candidate: constexpr auto tl::expected<T, E>::map(F&&) const && [with F = main()::<lambda(int)>&; T = int; E = int]
template <class F> constexpr auto map(F &&f) const && {
All other GCC 5 versions seem to work. I might need to not use ref-qualifiers for GCC5.5 or something.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.