31

The following code compiles in Clang but does not in GCC:

template<typename T>
struct Widget
{
    template<typename U>
    void foo(U)
    {
    }

    template<>
    void foo(int*)
    {
    }
};

According to the C++ standard ([temp.expl.spec], paragraph 2):

An explicit specialization may be declared in any scope in which the corresponding primary template may be defined

Is this a bug in GCC and if so how can I find it in its bug tracker?

This is GCC's output:

prog.cc:13:14: error: explicit specialization in non-namespace scope 'struct Widget<T>'
     template<>
              ^

I'm using GCC HEAD 8.0.1, with -std=c++2a.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
rubix_addict
  • 1,811
  • 13
  • 27

2 Answers2

31

This should be a GCC bug. Full specialization should be allowed in any scope, including in class definition.

According to CWG 727, [temp.expl.spec] paragraph 2 was changed from

(emphasis mine)

An explicit specialization shall be declared in a namespace enclosing the specialized template. An explicit specialization whose declarator-id or class-head-name is not qualified shall be declared in the nearest enclosing namespace of the template, or, if the namespace is inline (10.3.1 [namespace.def]), any namespace from its enclosing namespace set. Such a declaration may also be a definition. If the declaration is not a definition, the specialization may be defined later (10.3.1.2 [namespace.memdef]).

to

(emphasis mine)

An explicit specialization may be declared in any scope in which the corresponding primary template may be defined (10.3.1.2 [namespace.memdef], 12.2 [class.mem], 17.6.2 [temp.mem]).

It seems GCC fails to follow this.

EDIT

I have reported the issue as Bug 85282.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • 1
    This is a big change, no need any more of the defaulted dumb template parameter trick to simulate a an explicit template specialization :)!! – Oliv Apr 07 '18 at 14:35
  • 1
    Can you clarify which standard changes that? C++17? – HolyBlackCat Apr 09 '18 at 12:09
  • 1
    @HolyBlackCat Yes it's changed since C++17. You may check the different description in C++14(draft N4140) and C++17(draft N4659). – songyuanyao Apr 09 '18 at 14:27
  • 1
    @songyuanyao No, not really. The defect report has been retroactively applied to C++14. – rubix_addict Apr 11 '18 at 19:22
  • Great reference! After some testing, I found that Clang C++17 still does not allow it. Evidence : http://coliru.stacked-crooked.com/a/4477a9354f6cba2d Do you have any idea? Thank. – cppBeginner Jun 20 '18 at 08:23
  • @cppBeginner You meant Gcc? – songyuanyao Jun 20 '18 at 08:24
  • I am not sure. I notice both "clang++" and "g++" in the bottom of the coliru web page. I am new to this stuff. Thank. – cppBeginner Jun 20 '18 at 08:25
  • 3
    @cppBeginner I think Gcc is used; and yes, Gcc still doesn't fix this issue; [clang](https://wandbox.org/permlink/R3dM6pQpsNR5IWje) works fine. – songyuanyao Jun 20 '18 at 08:28
  • 1
    So far, this does not seem fixed and gcc dev claims that cwg727 never was approved. – Swift - Friday Pie Nov 22 '19 at 19:11
  • 1
    @Swift-FridayPie: No one has claimed that—only that it shouldn’t be allowed in language modes prior to `-std=c++17`. That said, I think *that’s* in error. – Davis Herring Jul 31 '20 at 01:09
  • @DavisHerring some compiler supported it for ages..at work I work with one from 2009 and it got C++17\C++14 features for templates or lambdas, like this syntax, but doesn't support variadic templates or range-based for! – Swift - Friday Pie Jul 31 '20 at 08:04
  • @Oliv, how does the "dumb template parameter trick" work? – fde-capu May 26 '22 at 13:11
  • @fde-capu Partial specialization was allowed. So let's say you declared a template with `template class X`, if you wanted to provide a specialization for X you could change the declaration to `template class X` and then provide a partial specialization `template class X` – Oliv May 26 '22 at 14:27
5

If somebody is looking for a workaround till this is fixed in gcc:

It is possible to use std::is_same_v and if constexpr godbolt

template<typename T>
struct Widget
{
    template<typename U>
    void foo(U)
    {
        if constexpr (std::is_same_v<U, int*>) {
            std::cout << "int*\n";
        }
        std::cout << "U\n";
    }

};
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
  • 1
    Thanks for this one. Seems to be the only one which works for member functions! :) Ugly if a lot of types are needed but still better to move all the code outside the class and add extra declarations. – TeaAge Solutions Jan 21 '23 at 10:01