Search code examples
c++17variadic-templatestemplate-meta-programmingautogeneric-lambda

C++ template code generation Error: use of 'some_variable' before deduction of 'auto'


I ran into some issues with this specific code. The problem has most likely something to do with pointer to member of type Harry stored in a tuple, and a vector with Harrytype variable since all other more simple variants do work.

The error that I get with g++:


main.cpp: In instantiation of 'abra(const std::vector<A>&, const std::tuple<_Elements ...>&) [with A = Harry; B = {int Harry::*, int* Harry::*}]::<lambda(const auto:1&)> [with auto:1 = int Harry::*]':

main.cpp:10:13:   required from 'void tuple_foreach_constexpr(const std::tuple<T ...>&, F) [with long unsigned int i = 0; long unsigned int size = 2; F = abra(const std::vector<A>&, const std::tuple<_Elements ...>&) [with A = Harry; B = {int Harry::*, int* Harry::*}]::<lambda(const auto:1&)>; T = {int Harry::*, int* Harry::*}]'

main.cpp:17:82:   required from 'void tuple_foreach_constexpr(const std::tuple<_Elements ...>&, F) [with F = abra(const std::vector<A>&, const std::tuple<_Elements ...>&) [with A = Harry; B = {int Harry::*, int* Harry::*}]::<lambda(const auto:1&)>; T = {int Harry::*, int* Harry::*}]'

main.cpp:29:32:   required from 'void abra(const std::vector<A>&, const std::tuple<_Elements ...>&) [with A = Harry; B = {int Harry::*, int* Harry::*}]'

main.cpp:56:27:   required from here

main.cpp:31:82: error: use of 'a' before deduction of 'auto'

             if constexpr(std::is_pointer<typename std::remove_reference<decltype(a.*x)>::type>::value)

                                                                                  ^

main.cpp:33:30: error: invalid type argument of unary '*' (have 'int')

                 std::cout << *(a.*x) << std::endl;

                              ^~~~~~~

main.cpp:6:6: error: 'void tuple_foreach_constexpr(const std::tuple<T ...>&, F) [with long unsigned int i = 1; long unsigned int size = 2; F = abra(const std::vector<A>&, const std::tuple<_Elements ...>&) [with A = Harry; B = {int Harry::*, int* Harry::*}]::<lambda(const auto:1&)>; T = {int Harry::*, int* Harry::*}]', declared using local type 'abra(const std::vector<A>&, const std::tuple<_Elements ...>&) [with A = Harry; B = {int Harry::*, int* Harry::*}]::<lambda(const auto:1&)>', is used but never defined [-fpermissive]

 void tuple_foreach_constexpr(const std::tuple<T...>& tuple, F func)

      ^~~~~~~~~~~~~~~~~~~~~~~

code:

#include <iostream>
#include <tuple>
#include <vector>

template<size_t i, size_t size, typename F, typename... T>
void tuple_foreach_constexpr(const std::tuple<T...>& tuple, F func)
{
    if constexpr(i<size)
    {
        func(std::get<i>(tuple));
        tuple_foreach_constexpr<i+1, size, F, T...>(tuple, func);
    }
}
template<typename F, typename... T>
void tuple_foreach_constexpr(const std::tuple<T...>& tuple, F func)
{
    tuple_foreach_constexpr<0, std::tuple_size<std::tuple<T...>>::value, F, T...>(tuple, func);
}

template<typename A, typename... B>
void abra
(
    const std::vector<A>& a_vector,
    const std::tuple<B...>& b_tuple
)
{
    for(const auto& a : a_vector)
    {
        tuple_foreach_constexpr(b_tuple, [&a](const auto &x)
        {
            if constexpr(std::is_pointer<typename std::remove_reference<decltype(a.*x)>::type>::value)
            {
                std::cout << *(a.*x) << std::endl;
            }
            else
            {
                std::cout << a.*x << std::endl;
            } // this does NOT work

            //std::cout << a.*x << std::endl; // this does work
        });
    }
}

struct Harry
{
    int a;
    int* b;
};    

int main()
{
    int m = 20;
    std::vector<Harry> h_vector = {Harry{10, &m}};
    std::tuple t_tuple = std::make_tuple(&Harry::a, &Harry::b);

    abra(h_vector, t_tuple);
}

It would be very nice if someone had some tips on how to solve this.

(I know this all looks like it makes no sense why anyone would need to do this. However, my priority is not to write good, usable code but to learn stuff and also I really want to get this architecture I had in mind to work.)


Solution

  • It would be very nice if someone had some tips on how to solve this.

    First of all: I reproduce your error with g++ but my clang++ (7.0.1) compile your code without problem.

    Who's right? g++ or clang++?

    I'm not a language lawyer and I'm not sure but I suspect it's a g++ bug.

    What is saying g++?

    It's saying that in this loop

    for(const auto& a : a_vector)
    {
        tuple_foreach_constexpr(b_tuple, [&a](const auto &x)
        {
            if constexpr(std::is_pointer<typename std::remove_reference<decltype(a.*x)>::type>::value)
            {
                std::cout << *(a.*x) << std::endl;
            }
            else
            {
                std::cout << a.*x << std::endl;
            } // this does NOT work
    
            //std::cout << a.*x << std::endl; // this does work
        });
    }
    

    the a variable, that is an auto variable (const auto& a : a_vector) so it's type must be deduced by the compiler, that is captured inside the lambda function, is used (decltype(a.*x)) before the deduction of the type.

    Anyway, the solution of the problem is simple: to make g++ happy, explicit the definition.

    You know that a is an element of a_vector that is defined as a std::vector<A> const &, so you know that a is a A const &.

    So, if you write the loop

    for ( A const & a : a_vector )
     {
       // ....
     }
    

    there is no more needs to deduce the type of a and your code compile also with g++.