3

I have a question about template specialization in conjunction with variadic templates and template template parameters/arguments.

The following little program compiles with Clang 6.0.1 and GCC 8.1.1 (target: x86_64-pc-linux-gnu).

#include <iostream>

struct IndexSlot3 {
    static constexpr std::size_t size = 3;
};

template<typename T_, typename... IndexSlots_>
struct Tensor {
    Tensor() { std::cout << "Order N picked" << std::endl; }
};
template<typename T_, typename IndexSlot0_>
struct Tensor<T_, IndexSlot0_> {
    Tensor() { std::cout << "Order 1 picked" << std::endl; }
};
template<typename T_, typename IndexSlot0_, typename IndexSlot1_>
struct Tensor<T_, IndexSlot0_, IndexSlot1_> {
    Tensor() { std::cout << "Order 2 picked" << std::endl; }
};

int main(int argc, char** argv) {
    Tensor<int, IndexSlot3> t1;
    Tensor<int, IndexSlot3, IndexSlot3> t2;
    Tensor<int, IndexSlot3, IndexSlot3, IndexSlot3> t3;

    return 0;
}

Compile commands:

clang++ -std=c++17 main.cpp -o main -Wall -Wpedantic
g++ -std=c++17 main.cpp -o main -Wall -Wpedantic

Running the program prints

Order 1 picked
Order 2 picked
Order N picked

with both compilers, as expected.

However, when I extend the above to the following program, where Tensor also takes a template template parameter defining storage, Clang picks the primary template in all three cases, while GCC still picks the specializations.

#include <iostream>

struct IndexSlot3 {
    static constexpr std::size_t size = 3;
};

template<typename T_, std::size_t... sizes>
struct CArrayStorage {
    using Type = T_*;
};
template<typename T_, std::size_t size0_>
struct CArrayStorage<T_, size0_> {
    using Type = T_[size0_];
};
template<typename T_, std::size_t size0_, std::size_t size1_>
struct CArrayStorage<T_, size0_, size1_> {
    using Type = T_[size0_][size1_];
};

template<typename T_,
        template<typename, std::size_t...> typename Storage_,
        typename... IndexSlots_>
struct Tensor {
    typename Storage_<T_, IndexSlots_::size...>::Type _storage;
    Tensor() { std::cout << "Order N picked" << std::endl; }
};
template<typename T_,
        template<typename, std::size_t> typename Storage_,
        typename IndexSlot0_>
struct Tensor<T_, Storage_, IndexSlot0_> {
    typename Storage_<T_, IndexSlot0_::size>::Type _storage;
    Tensor() { std::cout << "Order 1 picked" << std::endl; }
};
template<typename T_,
        template<typename, std::size_t, std::size_t> typename Storage_,
        typename IndexSlot0_, typename IndexSlot1_>
struct Tensor<T_, Storage_, IndexSlot0_, IndexSlot1_> {
    typename Storage_<T_, IndexSlot0_::size, IndexSlot1_::size>::Type _storage;
    Tensor() { std::cout << "Order 2 picked" << std::endl; }
};

int main(int argc, char** argv) {
    Tensor<int, CArrayStorage, IndexSlot3> t1;
    Tensor<int, CArrayStorage, IndexSlot3, IndexSlot3> t2;
    Tensor<int, CArrayStorage, IndexSlot3, IndexSlot3, IndexSlot3> t3;

    return 0;
}

Output with Clang:

Order N picked
Order N picked
Order N picked

Output with GCC:

Order 1 picked
Order 2 picked
Order N picked

I think GCC is right, because the specialization and the primary match equally well, so the specialization should be picked. But I'm not entirely sure how the introduction of the template template parameter changes things.

Should I file a bug against Clang? Against GCC? Against both? Have I wandered into the realm of undefined behaviour without realizing it?

Any help would be greatly appreciated.

[The examples aren't exactly minimal, but I wanted to provide a little bit of context, so you can understand what I'm trying to do.]

Student
  • 805
  • 1
  • 8
  • 11
anihilist
  • 31
  • 1
  • Here's another one, maybe more fitting: https://stackoverflow.com/questions/48446983/deducing-first-template-argument-with-other-template-parameters-defaulted – Rakete1111 Jul 25 '18 at 20:20
  • @Rakete1111 I agree it is a dupe of the second link---I suggest this question be closed. – Brian Bi Jul 25 '18 at 20:28
  • @Rakete1111 Thanks, that was quick :) I searched for over an hour, but didn't find these. Anyway, -frelaxed-template-template-args indeed fixes the issue. I'll try to understand exactly why tomorrow, and then I'll probably close this. – anihilist Jul 25 '18 at 20:32
  • @anihilist If you don't know the right keywords, there is no way to find dupes :) Some questions are really easy to find, but others, not so much - like this one. :( – Rakete1111 Jul 25 '18 at 20:39

0 Answers0