mardi 11 août 2020

Variadic templates, who is right? GCC or Clang?

I have following code:

enum class type
{
        zero,
        one,
        two
};

template <typename K>
using pv = std::pair<type, std::vector<K>>;

template <typename K>
using ps = std::pair<type, K>;


template <typename B, typename F, typename K>
void rec(int j, F &f, ps<K> const &s0) // 4
{
        auto [n, v] = s0;
        f(j, n, v);/*pro*/
        std::cout << std::endl;
}

template <typename B, typename F, typename K, typename... T>
void rec(int j, F &f, ps<K> const &s0, T const &... t) // 3
{
        auto [n, v] = s0;
        f(j, n, v);/*pro*/
        rec<B>(j, f, t...);
}

template <typename B, typename F, typename K, typename... T>
void rec(int j, F &f, pv<K> const &t0, ps<K> const &s0, T const &... t) // 2
{
        auto [n, v] = t0;
        for(auto k : v) {
                rec<B>(j, f, s0, t..., ps<K>{n, k});
        }
}

template <typename B, typename F, typename K, typename... T>
void rec(int j, F &f, pv<K> const &t0, T const &... t) // 1
{
        auto [n, v] = t0;
        for(auto k : v) {
                rec<B>(j, f, t..., ps<K>{n, k});
        }
}

void pro(int j, type t, std::string const &s)
{
        std::cout << j << ":" << t << ":" << s << ", ";
}

int main()
{
        std::vector<std::string> v0{"V00", "V01"};
        std::vector<std::string> v1{"V10", "V11"};
        std::vector<std::string> v2{"V20", "V21"};

        int j = 0;

        rec<char>(j, pro, pv<std::string>{type::zero, v0}, pv<std::string>{type::one, v1}, pv<std::string>{type::two, v2});

        return 0;
}

This compiles fine with clang++ (clang version 6.0.0-1ubuntu2) but fails with g++ (g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0). GCC throws the following error:

main.cpp: In function ‘int main()’:
main.cpp: error: call of overloaded ‘rec<char>(int&, void (&)(int, type, const string&), std::pair<type, std::vector<std::__cxx11::basic_string<char> > >, std::pair<type, std::vector<std::__cxx11::basic_string<char> > >, std::pair<type, std::vector<std::__cxx11::basic_string<char> > >)’ is ambiguous
  rec<char>(j, pro, pv<std::string>{type::zero, v0}, pv<std::string>{type::one, v1}, pv<std::string>{type::two, v2});
                                                                                                                   ^
main.cpp: note: candidate: void rec(int, F&, ps<K>&, const T& ...) [with B = char; F = void(int, type, const std::__cxx11::basic_string<char>&); K = std::vector<std::__cxx11::basic_string<char> >; T = {std::pair<type, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, std::pair<type, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >}; ps<K> = std::pair<type, std::vector<std::__cxx11::basic_string<char> > >]
 void rec(int j, F &f, ps<K> const &s0, T const &... t) // 3
      ^~~
main.cpp: note: candidate: void rec(int, F&, pv<K>&, const T& ...) [with B = char; F = void(int, type, const std::__cxx11::basic_string<char>&); K = std::__cxx11::basic_string<char>; T = {std::pair<type, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, std::pair<type, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >}; pv<K> = std::pair<type, std::vector<std::__cxx11::basic_string<char> > >]
 void rec(int j, F &f, pv<K> const &t0, T const &... t) // 1
      ^~~

Why are the functions at comment 1 and comment 3 ambiguous even though the parameter types pv and ps are different?

Interestingly, the following code works with both clang++ and g++ (note the removal of template parameter B from functions rec):

enum class type
{
        zero,
        one,
        two
};

template <typename K>
using pv = std::pair<type, std::vector<K>>;

template <typename K>
using ps = std::pair<type, K>;


template <typename F, typename K>
void rec(int j, F &f, ps<K> const &s0) // 4
{
        auto [n, v] = s0;
        f(j, n, v);/*pro*/
        std::cout << std::endl;
}

template <typename F, typename K, typename... T>
void rec(int j, F &f, ps<K> const &s0, T const &... t) // 3
{
        auto [n, v] = s0;
        f(j, n, v);/*pro*/
        rec(j, f, t...);
}

template <typename F, typename K, typename... T>
void rec(int j, F &f, pv<K> const &t0, ps<K> const &s0, T const &... t) // 2
{
        auto [n, v] = t0;
        for(auto k : v) {
                rec(j, f, s0, t..., ps<K>{n, k});
        }
}

template <typename F, typename K, typename... T>
void rec(int j, F &f, pv<K> const &t0, T const &... t) // 1
{
        auto [n, v] = t0;
        for(auto k : v) {
                rec(j, f, t..., ps<K>{n, k});
        }
}

void pro(int j, type t, std::string const &s)
{
        std::cout << j << ":" << t << ":" << s << ", ";
}

int main()
{
        std::vector<std::string> v0{"V00", "V01"};
        std::vector<std::string> v1{"V10", "V11"};
        std::vector<std::string> v2{"V20", "V21"};

        int j = 0;

        rec(j, pro, pv<std::string>{type::zero, v0}, pv<std::string>{type::one, v1}, pv<std::string>{type::two, v2});

        return 0;
}

Why does the second code work with g++ but not the first?

Aucun commentaire:

Enregistrer un commentaire