Inconsistent brace-or-equal initialization behavior between MSVC and Clang - c++

I have the following code which I compile on Visual Studio 2013 and Clang:
#include <memory>
template<typename T>
class foo
{
public:
typedef void (T::*CallbackFn)();
foo(T* mem, CallbackFn cb) : m_member(mem), m_cb(cb) {}
private:
T* m_member;
CallbackFn m_cb;
};
class another
{
private:
void callback() {}
public:
std::unique_ptr<foo<another>> f{new foo<another>(this, &another::callback)};
};
int main() {}
(Live sample here on Coliru)
When compiled on clang and GCC, this code works fine. However on VS2013 it fails with:
main.cpp(22): error C2276: '&' : illegal operation on bound member function expression
main.cpp(22): error C2664: 'foo<another>::foo(const foo<another> &)' : cannot convert argument 1 from 'another *const ' to 'const foo<another> &'
Reason: cannot convert from 'another *const ' to 'const foo<another>'
No constructor could take the source type, or constructor overload resolution was ambiguous
For some reason, VS compiler is trying to invoke the copy constructor of foo, which makes no sense at all. It's ignoring the 2nd parameter to the constructor entirely.
Interestingly if I change the constructor invocation of foo to braces like so:
std::unique_ptr<foo<another>> f{new foo<another>{this, &another::callback}};
It works just fine on MSVC. Can anyone explain this behavior? Is one way more correct than the other? Is this just another MSVC bug or due to some unsupported C++11 feature?

Can anyone explain this behavior?
Once the compiler encounters the first error, the rest is just junk. Ignore it. (I generally only look at the first compiler error that comes up, see here for VC++)
Is one way more correct than the other?
Both are completely equivalent in this context. MSVC simply fails to parse &another::callback and subsequently ignores it for further analysis of the line.

Related

Encounter the error: defaulted definition of default constructor is not constexpr when compile with clang-cl

I'm trying to compile these code with clang-cl with LLVM 15.
class Foo
{
public:
constexpr Foo() = default;
private:
int i;
};
int main(void)
{
Foo f;
}
The compilation command line is:
\> bazel build ... --compiler=clang-cl
The error will be displayed
cpp-constexpr/main.cpp(4,5): error: defaulted definition of default constructor is not constexpr
constexpr Foo() = default;
^
cpp-constexpr/main.cpp(13,9): error: no matching constructor for initialization of 'Foo'
Foo f;
^
cpp-constexpr/main.cpp(6,5): note: candidate constructor not viable: requires 1 argument, but 0 were provided
Foo(const Foo&) = delete;
^
2 errors generated.
Target //cpp-constexpr:constexpr failed to build
If it is compiled with MSVC 2022/2019, there will be no error at all. Looking forward to known your suggestions.
Thanks in advance.
TL;DR version: Compile to C++20 or a more recent version of the C++ Standard where both compilers should accept this code. Downside: you may find other discrepancies between their handling of C++20 and be no better off.
Explanation:
Quoting cppreference
for the constructor of a class or struct, every base class sub-object and every non-variant non-static data member must be initialized.
This is valid until C++20. As of C++20, the code should work as presented (but danged if I know what you'll do with a constant uninitialized variable i. Have to dig into the Standard or deeper into cppreference to see if i is zero initialized or something. This experiment with Matt Godbolt's compiler Explorer suggests it's not initialized.
So the fix is most likely to initialize i.
class Foo
{
public:
constexpr Foo() = default;
~Foo() = default;
Foo(const Foo&) = delete;
private:
int i = 0;
};
MSVC's ability to compile this code prior to C++20 appears to be a bug that could be fixed at any time. Rather than forcing incorrect behaviour onto clang-cl I recommend fixing the code (or compiling both sides to C++20 or more recent.

universal references and packed fields

Consider the following code:
#include <cstdint>
struct S {
uint32_t f1;
} __attribute__((packed)); // removing this attribute makes things work.
template <class T> void func(const T &x) {}
template <class T> void func(T &&x) {} // commenting out this line makes it "work"
int main() {
S s;
func(s.f1); // Error here in GCC, but not clang
}
GCC gives the following error:
<source>: In function 'int main()':
<source>:16:12: error: cannot bind packed field 's.S::f1' to 'unsigned int&'
16 | func(s.f1);
| ~~^~
It appears that GCC is choosing to not allow universal references to members of a packed struct, presumably due to alignment concerns. However, clang compiles the code just fine.
Adding to my confusion is that if I remove the (T &&x) overload, it works "just fine" if only the (const T &) overload exists. I would expect that if it can't use the universal-ref overload, then it would just fall back on const-ref version... but it doesn't.
Is clang incorrect here? Is GCC? Is it just undefined behavior and therefore they are both "right"?
The func(const T &x) is allowed because GCC will create a temporary to the packed member.
When adding a forwarding reference overload, the function call will resolve to a function that looks like func(uint32_t&). Since it's a mutable lvalue reference, no temporary can be created and the overload resolution fails, since there is no better match.
You can make it work by forcing const allowing the compiler to create a temporary again:
func(std::as_const(s).f1);

Ambiguous call to member function for captured this in a lambda

I have encountered an issue when trying to call a member function inside a lambda for a captured this. There is a const and non-const version of the function and it is templated on a type.
The following code demonstrates the error:
struct TEST
{
template <typename T>
void test() {}
template <typename T>
void test() const {}
TEST()
{
[this]()
{
test<void>();
}();
}
};
Messages: http://rextester.com/MLU2098
source_file.cpp(13): error C2668: 'TEST::test': ambiguous call to overloaded function
source_file.cpp(7): note: could be 'void TEST::test<void>(void) const'
source_file.cpp(4): note: or 'void TEST::test<void>(void)'
source_file.cpp(13): note: while trying to match the argument list '()'
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x64
I wasn't sure if this behaviour was correct and just an issue with the Microsoft compiler, so I tested the code with gcc and clang in the compiler explorer and they both compiled the code without an error.
Which compiler is displaying the correct behaviour here?
This is an issue with MSVC. The implicit this parameter has a cv-qualification. That's why overloading a member function on a cv-qualifier is possible. In the body of the c'tor, this points to a non-const object (initialization means we must modify the object after all).
This is enough to determine what overload to call.
For whatever reason, MSVC is confused. But if you call the member function by accessing the this pointer explicitly, the confusion vanishes:
void bar()
{
[this]()
{
this->test<void>();
}();
}
Live MSVC Example

Conversion is ambiguous in Visual Studio 2015, but not with clang

The following code, a simplified version of code found in the googlemock project, fails to compile in Visual Studio 2015 Update 1, but it compiles on clang [Apple LLVM version 7.0.0 (clang-700.1.76)].
struct ConvertibleFromAny
{
ConvertibleFromAny(int a_value);
template <typename T>
ConvertibleFromAny(const T& a_value);
};
template <typename T>
struct Matcher
{
Matcher(T value);
};
template <typename Rhs>
struct EqMatcher
{
explicit EqMatcher(const Rhs& rhs);
template <typename Lhs>
operator Matcher<Lhs>() const;
};
int main()
{
EqMatcher<ConvertibleFromAny> em(1);
Matcher<ConvertibleFromAny> m = em;
return 0;
}
The error occurs at the assignment statement
Matcher<ConvertibleFromAny> m = em;
and the error message is
error C2440: 'initializing': cannot convert from 'EqMatcher<ConvertibleFromAny>' to 'Matcher<ConvertibleFromAny>'
note: No constructor could take the source type, or constructor overload resolution was ambiguous
I can naively see an ambiguity between a member call to
EqMatcher<ConvertibleFromAny>::operator Matcher<ConvertibleFromAny>()
and an initialization conceptually similar to
Matcher<ConvertibleFromAny> m(ConvertibleFromAny<EqMatcher<ConvertibleFromAny>>(em))
My guess is that clang rules out the second option.
EDIT: Inspired by T.C.'s comment I tested the following:
struct A
{
};
struct X
{
X(const A&);
};
struct B
{
B(const X&);
};
int main()
{
A a;
B b = a;
}
It compiles with VS 2015, but not with clang. I have not been able to find any references that documents that the Visual C++ implementation intentionally deviates from the standard in this regard.
Is this a well-known problem?
Both your code samples produce the expected result with VS2015 Update 1, if I enable the "Disable Language Extensions" (/Za) flag. That is, the first one compiles, the second one does not.
I'm not sure which extension in particular is interfering here, though.
I found this MSDN page: Microsoft Extensions to C and C++, but it does not appear to be complete - for example, binding a non-const T& to an rvalue is not mentioned.
I have not been able to find any references that documents that the
Visual C++ implementation intentionally deviates from the standard in
this regard.
Here you go: Compiler Warning (level 1) C4928. The message is
illegal copy-initialization; more than one user-defined conversion has been implicitly applied
It also says this:
The compiler executed the code in all such routines.
So there's a de facto language extension that Microsoft has only barely documented.
You can use command line argument /we4928 to convert the warning into an error, effectively removing this single extension. See here for these arguments.

Inline member initializer containing pointer to member

At work, I'm experimenting a bit to bring some reflection into our codebase. Basically what I want to achieve, is to capture a pointer to data-member inside the type of the data-member's initializer:
template<class Class, int Class::*dataMember>
struct Reflect
{
operator int() {return 0;}
};
class Foo
{
public:
int bar = Reflect<Foo, &Foo::bar>{};
};
Although clang 3.4.1 (http://gcc.godbolt.org/) and Intel C++ XE 14.0 are able to compile this piece of code, when using MSVC12 I get the following error message:
error C2065: 'bar' : undeclared identifier
error C2975: 'dataMember' : invalid template argument for 'Reflect', expected compile-time constant expression
Furthermore, gcc 4.9.2 also seems to have trouble with it: http://ideone.com/ZUVOMO.
So my questions are:
Is the above piece of code valid C++11?
If yes, are there any work arounds for the failing compilers?
What VC++ complains about is certainly not a problem; [basic.scope.pdecl]/1,6:
The point of declaration for a name is immediately after its complete
declarator (Clause 8) and before its initializer (if any), except as
noted below.[…]
After the point of declaration of a class member, the member name can
be looked up in the scope of its class.
This implies that the name lookup is fine. However, as pointed out by #hvd in the comments, there are certain ambiguities in the grammar of such constructs.
Presumably GCC parses the above line until the comma:
int bar = Reflect<Foo,
// at this point Reflect < Foo can be a perfectly fine relational-expression.
// stuff after the comma could be a declarator for a second member.
And bails out once the rest is encountered.
A workaround that makes GCC happy is
int bar = decltype( Reflect<Foo, &Foo::bar>{} )();
Demo. This does not help with VC++ though, which apparently confuses the point of declaration as indicated by the error message.
Thus moving the initializer into a constructor will work:
int bar;
Foo() : bar( Reflect<Foo, &Foo::bar>{} ) {}
// (also works for GCC)
... while providing an initializer at the declaration of bar cannot. Demo #2 on rextester.