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

Should this compile?

48 views
Skip to first unread message

Daniel P

unread,
Jul 9, 2020, 11:34:53 PM7/9/20
to
#include <string>
#include <utility>
#include <type_traits>
#include <cstdint>

template <class T, class Enable = void>
class A
{
};

template<class CharT,class Traits,class Allocator>
class A<std::basic_string<CharT,Traits,Allocator>>
{
public:
template <class ChT = CharT>
typename std::enable_if<sizeof(ChT) == sizeof(uint8_t)>::type
f() const
{
}

template <class ChT = CharT>
typename std::enable_if<sizeof(ChT) != sizeof(uint8_t)>::type
f() const
{
A<std::string> convert;
convert.f(); // (*)
}
};

int main()
{
A<std::string> a;

a.f(); // (**)
}

With vs2019, Version 16.6.0, C++17, I get

Error C2039 'f': is not a member of 'A<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,void>'

at (*) and (**).

It compiles with earlier versions of vs.

Thanks,
Daniel

Ian Collins

unread,
Jul 10, 2020, 12:28:38 AM7/10/20
to
I can't see anything obviously wrong. Both clang-11 and gcc-9 compile
it without warnings with "-Wall -Wextra -pedantic -std=c++17"

--
Ian

Alf P. Steinbach

unread,
Jul 10, 2020, 2:49:13 AM7/10/20
to
On 10.07.2020 05:34, Daniel P wrote:
> #include <string>
> #include <utility>
> #include <type_traits>
> #include <cstdint>
>
> template <class T, class Enable = void>
> class A
> {
> };

I don't see the `Enable` parameter being used for anything?


> template<class CharT,class Traits,class Allocator>
> class A<std::basic_string<CharT,Traits,Allocator>>
> {
> public:
> template <class ChT = CharT>
> typename std::enable_if<sizeof(ChT) == sizeof(uint8_t)>::type
> f() const

Tip 1: using `sizeof(uint8_t)` does two things: checking that `uint8_t`
exists, and producing the compile time value 1.

I would separate those aspects.

In this code `uint8_t` is not formally guaranteed to exist even on an
8-bit byte machine, because there is no `using std::uint8_t`. However,
that's not the problem. Adding that declaration doesn't fix things.

Tip 2: instead of mind-boggling gymnastics to embed to `enable_if` in
the return type specification, just use a defaulted type parameter. It's
allowed on functions since C++11.


> {
> }
>
> template <class ChT = CharT>
> typename std::enable_if<sizeof(ChT) != sizeof(uint8_t)>::type
> f() const
> {
> A<std::string> convert;
> convert.f(); // (*)
> }
> };
>
> int main()
> {
> A<std::string> a;
>
> a.f(); // (**)
> }
>
> With vs2019, Version 16.6.0, C++17, I get

Huh, version 16.6.0?

Oh, the Visual Studio version. OK. The compiler with that version of VS
is version 14.26, with `_MSC_VER` = 1926, and with the command line
compiler reporting version number 19.26.28806.

The offset is due to the higher version number going all the way back to
Lattice C, the compiler that Microsoft built on for their first Visual C
(hence, compiler name "cl.exe", or so I believe), while the lower
version number only goes back to the introduction of Visual C++.


> Error C2039 'f': is not a member of 'A<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,void>'
>
> at (*) and (**).
>
> It compiles with earlier versions of vs.

It should compile. Wait a little, I'll check.


------------------------------------------------------------------------------------
[P:\temp]
> g++64 --version | find "++"
g++ (GCC) 9.2.0

[P:\temp]
> g++64 foo.cpp


[P:\temp]
> cl /nologo- 2>&1 | find "++"
Microsoft (R) C/C++ Optimizing Compiler Version 19.26.28806 for x64

[P:\temp]
> echo %CL%
/nologo /utf-8 /EHsc /GR /permissive- /FI"iso646.h" /std:c++17
/Zc:__cplusplus /W4 /wd4459 /D _CRT_SECURE_NO_WARNINGS /D
_STL_SECURE_NO_WARNINGS

[P:\temp]
> cl foo.cpp /Feb
foo.cpp
foo.cpp(26): error C2039: 'f': is not a member of
'A<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,void>'
foo.cpp(25): note: see declaration of
'A<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,void>'
foo.cpp(26): note: This diagnostic occurred in the compiler generated
function 'std::enable_if<sizeof(ChT)!=1,void>::type
A<std::basic_string<_Elem,_Traits,_Alloc>,void>::f(void) const'
foo.cpp(28): note: see reference to class template instantiation
'A<std::basic_string<_Elem,_Traits,_Alloc>,void>' being compiled
foo.cpp(34): error C2039: 'f': is not a member of
'A<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,void>'
foo.cpp(25): note: see declaration of
'A<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,void>'
------------------------------------------------------------------------------------


Hm!

I'm not sure what you're trying to do, but I would try something else,
with less templating.


- Alf

Daniel P

unread,
Jul 10, 2020, 8:53:15 AM7/10/20
to
On Friday, July 10, 2020 at 2:49:13 AM UTC-4, Alf P. Steinbach wrote:

> On 10.07.2020 05:34, Daniel P wrote:
> >
> > template <class ChT = CharT>
> > typename std::enable_if<sizeof(ChT) != sizeof(uint8_t)>::type
> > f() const
> > {
> > A<std::string> convert;
> > convert.f(); // (*)
> > }
> > };
> >
> > With vs2019, Version 16.6.0, C++17, I get
>
> Huh, version 16.6.0?
>
> Oh, the Visual Studio version. OK. The compiler with that version of VS
> is version 14.26, with `_MSC_VER` = 1926, and with the command line
> compiler reporting version number 19.26.28806.
>
> The offset is due to the higher version number going all the way back to
> Lattice C, the compiler that Microsoft built on for their first Visual C
> (hence, compiler name "cl.exe", or so I believe), while the lower
> version number only goes back to the introduction of Visual C++.
>
Thanks for the elucidation :-) I've never understood Microsoft's versioning,
or rather, as soon as I've figured it out, I've already forgotten it.
>
> > Error C2039 'f': is not a member of 'A<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,void>'
> >
> > at (*) and (**).
> >
> > It compiles with earlier versions of vs.
>
> It should compile. Wait a little, I'll check.
>
> foo.cpp(26): error C2039: 'f': is not a member of
> 'A<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,void>'
> foo.cpp(25): note: see declaration of
> 'A<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,void>'
> foo.cpp(26): note: This diagnostic occurred in the compiler generated
> function 'std::enable_if<sizeof(ChT)!=1,void>::type
> A<std::basic_string<_Elem,_Traits,_Alloc>,void>::f(void) const'
> foo.cpp(28): note: see reference to class template instantiation
> 'A<std::basic_string<_Elem,_Traits,_Alloc>,void>' being compiled
> foo.cpp(34): error C2039: 'f': is not a member of
> 'A<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,void>'
> foo.cpp(25): note: see declaration of
> 'A<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,void>'
> ------------------------------------------------------------------------------------
>
>
> Hm!
>
> ... I would try something else with less templating.
>
Thanks for verifying. For better or worse, the actual code has far more
templating.

After some experimenting, I discovered that if I change line (*) in

> >
> > template <class ChT = CharT>
> > typename std::enable_if<sizeof(ChT) != sizeof(uint8_t)>::type
> > f() const
> > {
> > A<std::string> convert; // (*)
> > convert.f();
> > }

to

A<std::string> convert(); // (**)

it does compile. On the other hand,

A<std::string> convert{};

does not.

I think I'll consider this a compiler bug with workaround.

Daniel

Manfred

unread,
Jul 10, 2020, 10:18:29 AM7/10/20
to
On 7/10/2020 2:53 PM, Daniel P wrote:
> After some experimenting, I discovered that if I change line (*) in
>
>>> template <class ChT = CharT>
>>> typename std::enable_if<sizeof(ChT) != sizeof(uint8_t)>::type
>>> f() const
>>> {
>>> A<std::string> convert; // (*)
>>> convert.f();
>>> }
> to
>
> A<std::string> convert(); // (**)
>
> it does compile. On the other hand,
>
> A<std::string> convert{};
>
> does not.
>
> I think I'll consider this a compiler bug with workaround.
>
> Daniel

A weird workaround: (**) is not an object declaration, it is a
/function/ declaration. This is the main reason the {} form (uniform
initialization) was introduced.
(gcc 9.3 compiles fine the original and the {} form, not the () form)

Daniel P

unread,
Jul 10, 2020, 10:52:27 AM7/10/20
to
On Friday, July 10, 2020 at 10:18:29 AM UTC-4, Manfred wrote:
> On 7/10/2020 2:53 PM, Daniel P wrote:
> > After some experimenting, I discovered that if I change line (*) in
> >
> >>> template <class ChT = CharT>
> >>> typename std::enable_if<sizeof(ChT) != sizeof(uint8_t)>::type
> >>> f() const
> >>> {
> >>> A<std::string> convert; // (*)
> >>> convert.f();
> >>> }
> > to
> >
> > A<std::string> convert(); // (**)
> >
> > it does compile. On the other hand,
> >
> > A<std::string> convert{};
> >
> > does not.
> >

> A weird workaround: (**) is not an object declaration, it is a
> /function/ declaration. This is the main reason the {} form (uniform
> initialization) was introduced.
> (gcc 9.3 compiles fine the original and the {} form, not the () form)

Indeed, in retrospect, I'm a little surprised that that _did_ compile in the
test case. In the real code, it did not. However, the "fix" below
succeeds, and does so as well in the real code.

I'm sure this is a vs2019 bug.

Daniel

#include <string>
#include <utility>
#include <type_traits>
#include <cstdint>

template <class T, class = void>
class A
{
};

template<class CharT,class Traits,class Allocator>
class A<std::basic_string<CharT,Traits,Allocator>>
{
int dummy_;
public:
A(int dummy = 0)
: dummy_(dummy)
{
}

template <class ChT = CharT>
typename std::enable_if<sizeof(ChT) == sizeof(uint8_t)>::type
f() const
{
}

template <class ChT = CharT>
typename std::enable_if<sizeof(ChT) != sizeof(uint8_t)>::type
f() const
{
A<std::string> convert{dummy_};

Pavel

unread,
Jul 12, 2020, 5:36:19 PM7/12/20
to
Daniel, I think it should compiler. To fix, I would try to replace
std::string with explicit specialization just in case Microsoft messed
it up with an additional template parameter or such (I do not have
VS2019 handy so cannot check myself). HTH. -Pavel

Jorgen Grahn

unread,
Jul 17, 2020, 2:12:19 AM7/17/20
to
On Fri, 2020-07-10, Alf P. Steinbach wrote:
...
> The offset is due to the higher version number going all the way back to
> Lattice C, the compiler that Microsoft built on for their first Visual C
> (hence, compiler name "cl.exe", or so I believe),

FWIW, the Lattice C compiler was named "lc", at least on the Amiga.
When it became SAS C, it was renamed "sc" there, according to my old
Makefiles.

> while the lower
> version number only goes back to the introduction of Visual C++.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
0 new messages