OrderProcessor::ProcessRange function in the code below accepts a range as an argument and iterates over it. When I try to call it with a range constructed with std::views::filter it does not compile, but when I copy and paste OrderProcessor::ProcessRange function implementation into the code it compiles:
#include <iostream>
#include <ranges>
#include <vector>
namespace data
{
struct Order
{
};
}
namespace app
{
template <class R, class Value>
concept range_over = std::ranges::range<R> &&
std::same_as<std::ranges::range_value_t<R>, Value>;
class OrderProcessor
{
public:
void Process(const data::Order&) {}
template <range_over<data::Order> Range>
void ProcessRange(const Range& range)
{
for (const data::Order& order : range)
{
Process(order);
}
}
};
}
int main()
{
using namespace app;
std::vector<data::Order> sample_orders;
auto range = sample_orders | std::views::filter([](const data::Order&) -> bool { return true; });
OrderProcessor processor;
processor.ProcessRange(range); //-does not compile
//But the following loop compiles:
//for (const data::Order& order : range)
//{
// processor.Process(order);
//}
}
it also compiles if I remove std::views::filter, what can be the difference?
GCC errors:
prog.cc: In instantiation of 'void app::OrderProcessor::ProcessRange(const Range&) [with Range = std::ranges::filter_view<std::ranges::ref_view<std::vector<data::Order> >, main()::<lambda(const data::Order&)> >]' :
prog.cc : 51 : 27 : required from here
prog.cc : 32 : 13 : error : passing 'const std::ranges::filter_view<std::ranges::ref_view<std::vector<data::Order> >, main()::<lambda(const data::Order&)> >' as 'this' argument discards qualifiers[-fpermissive]
32 | for (const data::Order& order : range)
| ^ ~~
In file included from prog.cc : 2 :
/ opt / wandbox / gcc - 11.1.0 / include / c++ / 11.1.0 / ranges : 1307 : 7 : note : in call to 'constexpr std::ranges::filter_view<_Vp, _Pred>::_Iterator std::ranges::filter_view<_Vp, _Pred>::begin() [with _Vp = std::ranges::ref_view<std::vector<data::Order> >; _Pred = main()::<lambda(const data::Order&)>]'
1307 | begin()
| ^ ~~~~
prog.cc : 32 : 13 : error : passing 'const std::ranges::filter_view<std::ranges::ref_view<std::vector<data::Order> >, main()::<lambda(const data::Order&)> >' as 'this' argument discards qualifiers[-fpermissive]
32 | for (const data::Order& order : range)
| ^ ~~
In file included from prog.cc : 2 :
/ opt / wandbox / gcc - 11.1.0 / include / c++ / 11.1.0 / ranges : 1321 : 7 : note : in call to 'constexpr auto std::ranges::filter_view<_Vp, _Pred>::end() [with _Vp = std::ranges::ref_view<std::vector<data::Order> >; _Pred = main()::<lambda(const data::Order&)>]'
1321 | end()
| ^ ~~
You just need to drop the const on the range argument to ProcessOrder(). A range may change when you iterate it - and apparently that's true for the filter view. Perhaps the library or the compiler could have guessed otherwise, but they don't.
Without the const - it compiles: https://godbolt.org/z/MnEczfTjG
Also note that if you make the range variable const to begin with, you'll be in trouble with your loop as well.
Related
Compare the following case when I have a class object that takes a vector. The non-deduced parameter T can be substituted fine with the default template argument:
#include <vector>
template <typename T = int>
struct container
{
container(std::vector<T> vec) {}
};
int main()
{
container C = std::vector{1,2,3,4,5};
}
This is not the case for my class which is a bit more complicated (CompilerExplorer):
#include <cstdio>
#include <initializer_list>
#include <variant>
template <size_t> struct obj;
template<size_t Vs>
using val = std::variant<std::monostate, int, struct obj<Vs>>;
template <size_t Vs = 0>
struct obj
{
obj() = default;
obj(std::initializer_list<val<Vs>> init) {
printf("initializer of object called, Vs = %d\n", Vs);
}
};
template <size_t Vs = 0>
struct container : public obj<Vs>
{
container(obj<0> init) {}
};
int main()
{
container<5> some_container = obj{1,2,5,2,obj{1,2,33},2,2};
}
This fails with the following error:
<source>: In function 'int main()':
<source>:29:57: error: class template argument deduction failed:
29 | container<5> some_container = obj{1,2,5,2,obj{1,2,33},2,2};
| ^
<source>:29:57: error: no matching function for call to 'obj(int, int, int)'
<source>:14:5: note: candidate: 'template<long unsigned int Vs> obj(std::initializer_list<std::variant<std::monostate, int, obj<Vs> > >)-> obj<<anonymous> >'
14 | obj(std::initializer_list<val<Vs>> init) {
| ^~~
But it works when I supplement the template specialization obj<0> in the instantiation of the container (in main). Any ideas why this doesn't work for my class and how I can fix it? I don't want to force the user to specify the template each time.
This problem already exists in the simpler case of just
auto o = obj{1,2,33};
which yields this error:
<source>:29:24: error: class template argument deduction failed:
29 | auto o = obj{1,2,33};
| ^
<source>:29:24: error: no matching function for call to 'obj(int, int, int)'
<source>:14:5: note: candidate: 'template<long unsigned int Vs> obj(std::initializer_list<std::variant<std::monostate, int, obj<Vs> > >)-> obj<<anonymous> >'
14 | obj(std::initializer_list<val<Vs>> init) {
| ^~~
<source>:14:5: note: template argument deduction/substitution failed:
<source>:29:24: note: mismatched types 'std::initializer_list<std::variant<std::monostate, int, obj<Vs> > >' and 'int'
29 | auto o = obj{1,2,33};
So, the compiler is unable to deduce, that the three ints should be an initializer list. If you add extra braces around them, the compiler recognizes that this should actually be a single list argument instead of three separate ones and it works:
auto o = obj{{1,2,33}};
This also carries over to the more complicated case:
container some_container = obj{{1,2,5,2,obj{{1,2,33}},2,2}};
This code works and generates the correct output (23, as 2 and 3 are the sizes of the two std::vector<int>s in array_of_vectors:
#include <array>
#include <boost/range/adaptor/transformed.hpp>
#include <iostream>
#include <vector>
using boost::adaptors::transformed;
constexpr auto get_size = [](auto const& snip){ return snip.size(); };
int main() {
std::array<std::vector<int>,2> array_of_vectors = {
{std::vector<int>{1,1}, std::vector<int>{1,1,1}}
};
auto array_of_sizes = array_of_vectors | transformed(get_size);
for (auto i : array_of_sizes) {
std::cout << i;
}
}
On the other hand, if I try to enforce that array_of_sizes is indeed a std::array<std::size_t, 2u> via boost::copy_range by changing this
auto array_of_sizes = array_of_vectors | transformed(get_size);
to this
auto array_of_sizes = boost::copy_range<std::array<std::size_t, 2u>>(
array_of_vectors | transformed(get_size)
);
I get the following error,
$ g++ -std=c++17 deleteme.cpp && ./a.out
In file included from /usr/include/boost/range/iterator_range.hpp:13,
from /usr/include/boost/range/adaptor/transformed.hpp:16,
from deleteme.cpp:2:
/usr/include/boost/range/iterator_range_core.hpp: In instantiation of ‘SeqT boost::copy_range(const Range&) [with SeqT = std::array<long unsigned int, 2>; Range = boost::range_detail::transformed_range<<lambda(const auto:1&)
2> >]’:
deleteme.cpp:16:114: required from here
/usr/include/boost/range/iterator_range_core.hpp:842:20: error: no matching function for call to ‘std::array<long unsigned int, 2>::array(boost::range_detail::extract_const_iterator<boost::range_detail::transformed_range<<la
y<std::vector<int>, 2> >, true>::type, boost::range_detail::extract_const_iterator<boost::range_detail::transformed_range<<lambda(const auto:1&)>, std::array<std::vector<int>, 2> >, true>::type)’
842 | return SeqT( boost::begin( r ), boost::end( r ) );
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from deleteme.cpp:1:
/usr/include/c++/10.2.0/array:94:12: note: candidate: ‘std::array<long unsigned int, 2>::array()’
94 | struct array
| ^~~~~
/usr/include/c++/10.2.0/array:94:12: note: candidate expects 0 arguments, 2 provided
/usr/include/c++/10.2.0/array:94:12: note: candidate: ‘constexpr std::array<long unsigned int, 2>::array(const std::array<long unsigned int, 2>&)’
/usr/include/c++/10.2.0/array:94:12: note: candidate expects 1 argument, 2 provided
/usr/include/c++/10.2.0/array:94:12: note: candidate: ‘constexpr std::array<long unsigned int, 2>::array(std::array<long unsigned int, 2>&&)’
/usr/include/c++/10.2.0/array:94:12: note: candidate expects 1 argument, 2 provided
On the other hand, most surprisingly to me (!), if I force array_of_sizes to be a std::vector<std::size_t>, e.g.
auto array_of_sizes = boost::copy_range<std::vector<std::size_t>>( // vector instead of array
array_of_vectors | transformed(get_size)
);
it works! It looks like transformed turns a std::array<T,N> into a range convertible to std::vector<T>, just like it loses track of std::array's compile-time size.
What is this error/behavior due to?
I can only think that something is not working because of std::array's compile-time known size, as opposed to std::vector's run-time size. Probably transformed can't deal with that? Or maybe it's copy_range which can't?
If you compile with -std=c++2a, gcc 10.2 has a nice error message:
boost_1_74_0/boost/range/iterator_range_core.hpp:842:38: error: array must be initialized with a brace-enclosed initializer
842 | return SeqT( boost::begin( r ), boost::end( r ) );
| ~~~~~~~~~~~~^~~~~
The problem is that boost::copy_range only knows how to construct containers that take an iterator pair as (one of) their constructor parameters, so it is incompatible with std::array which does not actually have any constructors - it is only constructible as an aggregate from a (braced) list of elements.
With C++20's lambdas, you could write an alternate function that uses simple metaprogramming to produce that list of elements:
template<class T, class R>
T copy_range_fixed_size(R const& r) {
return [&r]<std::size_t... I>(std::index_sequence<I...>) {
auto it = boost::begin(r);
return T{(I, *it++)...};
}(std::make_index_sequence<T{}.size()>{});
}
Example.
I want to search an input range for an element that has a certain value in a member via an accessor.
range-v3 documentation is... thin, but there are sources such as this answer by AFAIK the 2 main range-v3 developers indicates this kind of thing should Just Work, albeit with sort not find.
Given the code, compiled with g++ -std=c++17 against ericniebler/range-v3 release range-v3-0.10.0, using g++.exe (Rev2, Built by MSYS2 project) 9.3.0:
#include <range/v3/algorithm/find.hpp>
#include <vector>
auto
main() -> int
{
struct S {
int i{};
auto get_i() const { return i; }
};
auto const ss = std::vector<S>(10);
ranges::find(ss, 1, &S::get_i);
return 0;
}
I get a spew of errors:
test.cpp: In function 'int main()':
test.cpp:14:31: error: no match for call to '(const ranges::find_fn) (const std::vector<main()::S>&, int, int (main()::S::*)() const)'
14 | ranges::find(ss, 1, &S::get_i);
| ^
In file included from FOO/include/range-v3/range/v3/range_fwd.hpp:24,
from FOO/include/range-v3/range/v3/algorithm/find.hpp:18,
from test.cpp:1:
FOO/include/range-v3/range/v3/detail/config.hpp:618:27: note: candidate: 'template<class I, class S, class V, class P> constexpr concepts::return_t<I, typename std::enable_if<(((input_iterator<I> && sentinel_for<S, I>) && indirect_relation<ranges::equal_to, typename ranges::detail::select_projected_<P>::apply<I>, const V*>) && concepts::detail::CPP_true(concepts::detail::Nil{})), void>::type> ranges::find_fn::operator()(I, S, const V&, P) const'
618 | #define RANGES_FUNC(NAME) operator() RANGES_FUNC_CONST_ /**/
| ^~~~~~~~
FOO/include/range-v3/range/v3/algorithm/find.hpp:47:24: note: in expansion of macro 'RANGES_FUNC'
47 | constexpr auto RANGES_FUNC(find)(I first, S last, V const & val, P proj = P{})
| ^~~~~~~~~~~
FOO/include/range-v3/range/v3/detail/config.hpp:618:27: note: template argument deduction/substitution failed:
618 | #define RANGES_FUNC(NAME) operator() RANGES_FUNC_CONST_ /**/
| ^~~~~~~~
FOO/include/range-v3/range/v3/algorithm/find.hpp:47:24: note: in expansion of macro 'RANGES_FUNC'
47 | constexpr auto RANGES_FUNC(find)(I first, S last, V const & val, P proj = P{})
| ^~~~~~~~~~~
In file included from FOO/include/range-v3/range/v3/iterator/access.hpp:22,
from FOO/include/range-v3/range/v3/iterator/concepts.hpp:30,
from FOO/include/range-v3/range/v3/algorithm/find.hpp:22,
from test.cpp:1:
FOO/include/range-v3/std/detail/associated_types.hpp: In substitution of 'template<bool B, class T> using enable_if_t = typename ranges::detail::enable_if::apply<T> [with bool B = ranges::readable<std::vector<main()::S> >; T = std::vector<main()::S>]':
FOO/include/range-v3/range/v3/iterator/concepts.hpp:561:19: required by substitution of 'template<class I> using apply = ranges::detail::enable_if_t<(bool)(readable<I>), I> [with I = std::vector<main()::S>]'
FOO/include/range-v3/range/v3/algorithm/find.hpp:48:15: required by substitution of 'template<class I, class S, class V, class P> constexpr concepts::return_t<I, typename std::enable_if<(((input_iterator<I> && sentinel_for<S, I>) && indirect_relation<ranges::equal_to, typename ranges::detail::select_projected_<P>::apply<I>, const V*>) && concepts::detail::CPP_true(concepts::detail::Nil{})), void>::type> ranges::find_fn::operator()(I, S, const V&, P) const [with I = std::vector<main()::S>; S = int; V = int (main()::S::*)() const; P = ranges::identity]'
test.cpp:14:31: required from here
FOO/include/range-v3/std/detail/associated_types.hpp:73:15: error: no class template named 'apply' in 'struct ranges::detail::enable_if<false>'
73 | using enable_if_t = typename enable_if<B>::template apply<T>;
| ^~~~~~~~~~~
In file included from FOO/include/range-v3/range/v3/range_fwd.hpp:24,
from FOO/include/range-v3/range/v3/algorithm/find.hpp:18,
from test.cpp:1:
FOO/include/range-v3/range/v3/detail/config.hpp:618:27: note: candidate: 'template<class Rng, class V, class P> constexpr concepts::return_t<typename ranges::detail::if_then<forwarding_range_<R> >::apply<decltype (ranges::_::begin(declval<Rng&>())), ranges::dangling>, typename std::enable_if<((input_range<Rng> && indirect_relation<ranges::equal_to, typename ranges::detail::select_projected_<P1>::apply<decltype (ranges::_::begin(declval<Rng&>()))>, const V*>) && concepts::detail::CPP_true(concepts::detail::Nil{})), void>::type> ranges::find_fn::operator()(Rng&&, const V&, P) const'
618 | #define RANGES_FUNC(NAME) operator() RANGES_FUNC_CONST_ /**/
| ^~~~~~~~
FOO/include/range-v3/range/v3/algorithm/find.hpp:60:24: note: in expansion of macro 'RANGES_FUNC'
60 | constexpr auto RANGES_FUNC(find)(Rng && rng, V const & val, P proj = P{})
| ^~~~~~~~~~~
FOO/include/range-v3/range/v3/detail/config.hpp:618:27: note: template argument deduction/substitution failed:
618 | #define RANGES_FUNC(NAME) operator() RANGES_FUNC_CONST_ /**/
| ^~~~~~~~
FOO/include/range-v3/range/v3/algorithm/find.hpp:60:24: note: in expansion of macro 'RANGES_FUNC'
60 | constexpr auto RANGES_FUNC(find)(Rng && rng, V const & val, P proj = P{})
| ^~~~~~~~~~~
In file included from FOO/include/range-v3/range/v3/iterator/access.hpp:22,
from FOO/include/range-v3/range/v3/iterator/concepts.hpp:30,
from FOO/include/range-v3/range/v3/algorithm/find.hpp:22,
from test.cpp:1:
FOO/include/range-v3/std/detail/associated_types.hpp: In substitution of 'template<bool B, class T> using enable_if_t = typename ranges::detail::enable_if::apply<T> [with bool B = ranges::indirectly_regular_unary_invocable<int (main()::S::*)() const, __gnu_cxx::__normal_iterator<const main()::S*, std::vector<main()::S> > >; T = ranges::detail::projected_<__gnu_cxx::__normal_iterator<const main()::S*, std::vector<main()::S> >, int (main()::S::*)() const>]':
FOO/include/range-v3/range/v3/iterator/concepts.hpp:552:19: required by substitution of 'template<class Proj> template<class I> using apply = ranges::detail::enable_if_t<(bool)(indirectly_regular_unary_invocable<Proj, I>), ranges::detail::projected_<I, Proj> > [with I = __gnu_cxx::__normal_iterator<const main()::S*, std::vector<main()::S> >; Proj = int (main()::S::*)() const]'
FOO/include/range-v3/range/v3/algorithm/find.hpp:61:15: required by substitution of 'template<class Rng, class V, class P> constexpr concepts::return_t<typename ranges::detail::if_then<forwarding_range_<R> >::apply<decltype (ranges::_::begin(declval<Rng&>())), ranges::dangling>, typename std::enable_if<((input_range<Rng> && indirect_relation<ranges::equal_to, typename ranges::detail::select_projected_<P1>::apply<decltype (ranges::_::begin(declval<Rng&>()))>, const V*>) && concepts::detail::CPP_true(concepts::detail::Nil{})), void>::type> ranges::find_fn::operator()(Rng&&, const V&, P) const [with Rng = const std::vector<main()::S>&; V = int; P = int (main()::S::*)() const]'
test.cpp:14:31: required from here
FOO/include/range-v3/std/detail/associated_types.hpp:73:15: error: no class template named 'apply' in 'struct ranges::detail::enable_if<false>'
73 | using enable_if_t = typename enable_if<B>::template apply<T>;
| ^~~~~~~~~~~
shell returned 1
Making the projection a lambda...
ranges::find( ss, 1, [](auto const& s){ return s.get_i(); } );
...works but seems wasted typing.
Referring directly to the data member...
ranges::find(ss, 1, &S::i);
...works but is not possible if it should be encapsulated behind a const getter, transformer, etc.
What am I doing wrong? Can I not use a pointer-to-member-function as projection? Is it intended?
Edit: clang++ (also on MSYS2) does work here. So I guess this must be a bug in g++. Off to Bugzilla I go... edit: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94973
This is caused by GCC on Windows defaulting to -fms-extensions for compatibility with Microsoft compilers, and that pulling in a non-standard extension that introduces ambiguity between pointers-to-member-data vs -functions written as instance.*member.
We can pass -fno-ms-extensions to stop this and compile such code succesfully, until GCC remove that particular extension, which seems unnecessary and unhelpful nowadays.
Jonathan answered this on my GCC bug 94973:
Jonathan Wakely 2020-05-06 16:26:58 UTC
Aha, the same problem happens on linux if I compile with -fms-extensions
This is the old MS extension that causes x.*f to be accepted when f is a pointer to member function, which should only be valid when used as (x.*f)().
That causes ranges::invoke to think that the projection is a pointer to data member, when actually it's a pointer to member function.
See also PR 94771 comment 4.
Jason, do we want to disable that extension in SFINAE contexts?
Jonathan Wakely 2020-05-06 16:47:42 UTC
They're on by default for mingw, for compatibility with the MS compiler (but in this case it seems the relevant extension is ancient history).
The following code https://godbolt.org/z/X0na5H fails to compile the rvalue version of the following code. I've used the BOOST_FOREACH extensibility guidelines I found here
In short I can't seem to get boost_foreach working with noncopyabe rvalues. Is this even possible?
#include <boost/optional.hpp>
#include <boost/foreach.hpp>
#include <vector>
struct IdentityOp : std::vector<int>, boost::noncopyable {
IdentityOp(std::vector<int> && v):std::vector<int>(std::move(v)){}
};
namespace boost { namespace foreach
{
template <>
struct is_noncopyable< IdentityOp >
: mpl::true_
{
};
}}
#define BGS_TEST_LVALUE
// At global scope...
inline boost::mpl::true_ *
boost_foreach_is_noncopyable( IdentityOp *&, boost::foreach::tag )
{
return 0;
}
int main(){
std::vector<int> v;
#ifdef BGS_TEST_LVALUE
// LVALUE VERSION
IdentityOp op(std::move(v));
BOOST_FOREACH(const auto & a, op)
#else
// RVALUE VERSION
BOOST_FOREACH(const auto & a, IdentityOp(std::move(v)))
#endif
{}
}
shows the problem. If I define BGS_TEST_LVALUE then the code compiles. If I don't define it I get the error.
In file included from <source>:2:
/celibs/boost_1_70_0/boost/foreach.hpp: In instantiation of 'boost::foreach_detail_::auto_any<T> boost::foreach_detail_::contain(const T&, mpl_::true_*) [with T = IdentityOp; mpl_::true_ = mpl_::bool_<true>]':
<source>:34:5: required from here
/celibs/boost_1_70_0/boost/foreach.hpp:632:25: error: use of deleted function 'boost::foreach_detail_::auto_any<IdentityOp>::auto_any(boost::foreach_detail_::auto_any<IdentityOp>&&)'
632 | return auto_any<T>(t);
| ^
/celibs/boost_1_70_0/boost/foreach.hpp:253:8: note: 'boost::foreach_detail_::auto_any<IdentityOp>::auto_any(boost::foreach_detail_::auto_any<IdentityOp>&&)' is implicitly deleted because the default definition would be ill-formed:
253 | struct auto_any : auto_any_base
| ^~~~~~~~
/celibs/boost_1_70_0/boost/foreach.hpp:253:8: error: call of overloaded 'IdentityOp(IdentityOp)' is ambiguous
<source>:8:5: note: candidate: 'IdentityOp::IdentityOp(std::vector<int>&&)'
8 | IdentityOp(std::vector<int> && v):std::vector<int>(std::move(v)){}
| ^~~~~~~~~~
<source>:7:9: note: candidate: 'IdentityOp::IdentityOp(const IdentityOp&)' <deleted>
7 | struct IdentityOp : std::vector<int>, boost::noncopyable {
| ^~~~~~~~~~
<source>:7:9: note: candidate: 'IdentityOp::IdentityOp(IdentityOp&&)' <deleted>
In file included from <source>:2:
/celibs/boost_1_70_0/boost/foreach.hpp: In instantiation of 'boost::foreach_detail_::auto_any<T>::auto_any(const T&) [with T = IdentityOp]':
/celibs/boost_1_70_0/boost/foreach.hpp:632:12: required from 'boost::foreach_detail_::auto_any<T> boost::foreach_detail_::contain(const T&, mpl_::true_*) [with T = IdentityOp; mpl_::true_ = mpl_::bool_<true>]'
<source>:34:5: required from here
/celibs/boost_1_70_0/boost/foreach.hpp:256:15: error: use of deleted function 'IdentityOp::IdentityOp(const IdentityOp&)'
256 | : item(t)
| ^
<source>:7:9: note: 'IdentityOp::IdentityOp(const IdentityOp&)' is implicitly deleted because the default definition would be ill-formed:
7 | struct IdentityOp : std::vector<int>, boost::noncopyable {
| ^~~~~~~~~~
<source>:7:9: error: use of deleted function 'boost::noncopyable_::noncopyable::noncopyable(const boost::noncopyable_::noncopyable&)'
In file included from /celibs/boost_1_70_0/boost/noncopyable.hpp:15,
from /celibs/boost_1_70_0/boost/foreach.hpp:72,
from <source>:2:
/celibs/boost_1_70_0/boost/core/noncopyable.hpp:49:7: note: declared here
49 | noncopyable( const noncopyable& ) = delete;
| ^~~~~~~~~~~
Compiler returned: 1
https://godbolt.org/z/X0na5H
Consider the following code:
#include <unordered_map>
#include <tuple>
namespace Test
{
template<typename State>
struct StateTableEntry
{
State state;
};
template<typename State>
using StateRow = std::unordered_map<int,StateTableEntry<State>>;
template<typename StateRowValueType>
auto& entryBAD(StateRowValueType& row)
{ return row.second; }
template<typename StateRowValueType>
auto entryOK(StateRowValueType& row) -> decltype((row.second))
{ return row.second; }
}
template<class T,int I,class O> std::enable_if_t<I==std::tuple_size<T>::value>
for_each_index_of(O&){}
template<class Tuple, int startingIndex=0, class Operation>
std::enable_if_t<startingIndex<std::tuple_size<Tuple>::value>
for_each_index_of(const Operation& operation)
{
operation(std::integral_constant<std::size_t,startingIndex>());
for_each_index_of<Tuple,startingIndex+1>(operation);
}
int main()
{
for_each_index_of<std::tuple<int>>([](const auto&)
{
Test::StateRow<long> stateRow;
for(auto& rowElement : stateRow)
{
auto& state(entryBAD(rowElement).state);
state=1;
}
});
}
If I try to compile it as is, gcc tells me
test.cpp: In instantiation of ‘main()::<lambda(const auto:1&)> [with auto:1 = std::integral_constant<long unsigned int, 0ul>]’:
test.cpp:29:14: required from ‘std::enable_if_t<(startingIndex < std::tuple_size<_Tp>::value)> for_each_index_of(const Operation&) [with Tuple = std::tuple<int>; int startingIndex = 0; Operation = main()::<lambda(const auto:1&)>; std::enable_if_t<(startingIndex < std::tuple_size<_Tp>::value)> = void]’
test.cpp:43:6: required from here
test.cpp:40:44: error: use of ‘template<class StateRowValueType> auto& Test::entryBAD(StateRowValueType&)’ before deduction of ‘auto’
auto& state(entryBAD(rowElement).state);
^
test.cpp:40:44: error: use of ‘auto& Test::entryBAD(StateRowValueType&) [with StateRowValueType = std::pair<const int, Test::StateTableEntry<long int> >]’ before deduction of ‘auto’
test.cpp:24:1: error: ‘std::enable_if_t<(I == std::tuple_size<_Tp>::value)> for_each_index_of(O&) [with T = std::tuple<int>; int I = 1; O = const main()::<lambda(const auto:1&)>; std::enable_if_t<(I == std::tuple_size<_Tp>::value)> = void]’, declared using local type ‘const main()::<lambda(const auto:1&)>’, is used but never defined [-fpermissive]
for_each_index_of(O&){}
^
But if I move the conde of lambda out of the lambda or replace call of entryBAD with entryOK, for some reason compilation succeeds. Same success if I move definition of entryBAD out of namespace Test.
Also, clang++ 3.6 compiles in all cases without complaints.
Is gcc right or is it a bug in it? If gcc is right, then what's wrong with the code?