Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

VS2019 and template specialisation

45 views
Skip to first unread message

James Lothian

unread,
Mar 23, 2021, 7:22:19 PM3/23/21
to
Given the following code:

template <int L, int M>
class Unit
{};

template <class U1, class U2>
struct Mul
{};

template <int... L, int... R>
struct Mul<Unit<L...>, Unit<R...> >
{
typedef Unit<L + R...> Result;
};

typedef Unit<1, 0> Length;
typedef Unit<0, 1> Mass;

// Error here
typedef typename Mul<Length, Mass>::Result LM;

int main()
{
LM lm;
return 0;
}

Visual Studio 2019 and 2015 both say:
C:\Users\James\Documents\SpecTest\SpecTest.cpp(21,37): error C2039:
'Result': is not a member of 'Mul<Length,Mass>'
C:\Users\James\Documents\SpecTest\SpecTest.cpp(21): message : see
declaration of 'Mul<Length,Mass>'

Apparently, VC++ picks the base case of the template rather than the
specialisation.
Every online c++ compiler I've managed to find (presumably using gcc)
accepts this code. So, who's right? Am I doing something stupid here?

Thanks,
James

Alf P. Steinbach

unread,
Mar 24, 2021, 10:21:18 AM3/24/21
to
On 24.03.2021 00:22, James Lothian wrote:
> template <int L, int M>
> class Unit
> {};
>
> template <class U1, class U2>
> struct Mul
> {};
>
> template <int... L, int... R>
> struct Mul<Unit<L...>, Unit<R...> >
> {
>     typedef Unit<L + R...> Result;
> };
>
> typedef Unit<1, 0> Length;
> typedef Unit<0, 1> Mass;
>
> // Error here
> typedef typename Mul<Length, Mass>::Result LM;
>
> int main()
> {
>     LM lm;
>     return 0;
> }

Looks like a Visual C++ bug. And it looks like a bug related to
parameter pack handling, and not related to (lack of) two-phase template
compilation. I think you should report it.

Workaround:

---------------------------------------------
template< int L, int M >
class Unit {};

template< class U1, class U2 >
struct Mul;

template< int L1, int L2, int R1, int R2 >
struct Mul<Unit<L1, L2>, Unit<R1, R2> >
{
using Result = Unit<L1 + R1, L2 + R2>;
};

using Length = Unit<1, 0>;
using Mass = Unit<0, 1>;

using LM = typename Mul<Length, Mass>::Result;

auto main() -> int
{
LM lm;
(void) lm;
}
---------------------------------------------

However, to my eyes the units computation doesn't seem correct. I don't
know if Boost Units does this correctly, but I think it must. So I
recommend using Boost Units instead.

<url: https://www.boost.org/doc/libs/1_65_0/doc/html/boost_units.html>


- Alf

James Lothian

unread,
Mar 24, 2021, 3:25:37 PM3/24/21
to
Alf P. Steinbach wrote:

> Looks like a Visual C++ bug. And it looks like a bug related to
> parameter pack handling, and not related to (lack of) two-phase template
> compilation. I think you should report it.
>

Thank you, that's pretty much what I thought.
> Workaround:

This is a much distilled-down version of something a good bit
bigger. I know I can work round this by avoiding the parameter pack and
fixing the number of dimensions in a Unit, but this means that when I
add another dimension to my units, I have to modify Mul (and Div, and
Exp, and so on). I've found that the following creeping horror works:

template <class U1, class U2>
struct Mul
{
private:
template <int... L, int... R>
static auto mulGuts(Unit<L...>, Unit<R...>)
{
return Unit<L + R...>();
}

public:
typedef decltype(mulGuts(U1(), U2())) Result;
};

but it does creep and it is horrible.

>
> However, to my eyes the units computation doesn't seem correct. I don't
> know if Boost Units does this correctly, but I think it must. So I
> recommend using Boost Units instead.
>
> <url: https://www.boost.org/doc/libs/1_65_0/doc/html/boost_units.html>

But where's the fun in that :-)

Thanks,
James

MrSpook_t...@8ihqvjzp9.gov

unread,
Mar 25, 2021, 4:58:58 AM3/25/21
to
On Wed, 24 Mar 2021 19:25:24 +0000
James Lothian <jamesl...@gmail.com> wrote:
>template <class U1, class U2>
>struct Mul
>{
>private:
> template <int... L, int... R>
> static auto mulGuts(Unit<L...>, Unit<R...>)
> {
> return Unit<L + R...>();
> }
>
>public:
> typedef decltype(mulGuts(U1(), U2())) Result;
>};
>
>but it does creep and it is horrible.

I can't help thinking that if you need syntax that mangled perhaps you should
revisit your design.

James Lothian

unread,
Mar 25, 2021, 9:57:18 AM3/25/21
to
I wouldn't need syntax as mangled as this if I weren't working around a
deficiency in VS2019.

James

Andrey Tarasevich

unread,
Apr 23, 2021, 3:17:07 PM4/23/21
to
On 3/23/2021 4:22 PM, James Lothian wrote:
> Every online c++ compiler I've managed to find (presumably using gcc)
> accepts this code. So, who's right? Am I doing something stupid here?

Yes, it appears to be a bug in MSVC++. It appears that if you separate
the first parameter from each pack, it serves as a workaround and MSVC++
choses the proper specialization

template <int L1, int... L, int R1, int... R>
struct Mul<Unit<L1, L...>, Unit<R1, R...> >
{
typedef Unit<L1 + R1, L + R...> Result;
};

--
Best regards,
Andrey Tarasevich



James Lothian

unread,
Apr 27, 2021, 7:28:31 AM4/27/21
to
Thanks for this -- it's less horrible than the workaround I've been using.

James

0 new messages