Lambdas in variadic templates - c++

Using Microsoft Visual C++ 2013 (12.0), I am encountering compile-time errors when using a lambda in a constructor in a variadic template. I have managed to boil it down as shown below (see the lines with the error comments). It appears to be a bug in 12.0 that is not present in 14.0. I haven't tried other versions. Is there any documentation on this bug, perhaps in the form of a release note that clarifies the conditions under which this bug occurs and which states that it has been explicitly fixed?
#include <functional>
// a simple method that can take a lambda
void MyFunction(const std::function<void()>& f) {}
// a simple class that can take a lambda
class MyClass
{
public:
MyClass(const std::function<void()>& f) {}
};
// non-templated test
void test1()
{
MyFunction([] {}); // OK
MyClass([] {}); // OK
MyClass o([] {}); // OK
}
// non-variadic template test
template<typename T>
void test2()
{
MyFunction([] {}); // OK
MyClass([] {}); // OK
MyClass o([] {}); // OK
}
// variadic template test
template<typename... T>
void test3()
{
MyFunction([] {}); // OK
MyClass([] {}); // OK
MyClass a([] {}); // error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
// error C2440: 'initializing' : cannot convert from 'test3::<lambda_12595f14a5437138aca1906ad0f32cb0>' to 'int'
MyClass b(([] {})); // putting the lambda in an extra () seems to fix the problem
}
// a function using the templates above must be present
int main()
{
test1();
test2<int>();
test3<int, int, int>();
return 1;
}
Edit/Update: MSVC 2013 compiler seem to have this bug, latest versions fixed that. GCC and clang compilers don't show any error.

I can't say is that bug or not, back days Microsoft was not so eager to latest C++ standards, it was two steps behind GCC and CLang, and it's not a surprise to know that no-one implement a whole standard in one compiler update. Some stuff from std library may be partially implemented, for example Visual Studio usually implements new features under std::experimental namespace (filesystem for VS2013), but something like compiler lexemes can't be added that way.
For common information about standard compatibility I suggest to use compiler support article https://en.cppreference.com/w/cpp/compiler_support/11 (MSVC means _MSC_VER definition) and
https://learn.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance more for detailed information about Visual Studio. Here https://en.wikipedia.org/wiki/Microsoft_Visual_C++#Internal_version_numbering you can find _MSC_VER version along with related Visual Studio version, source is https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros.
Based on those articles I believe that your problem lays in between "Variadic templates" and "Lambda expression" both are fully supported from Visual Studio 19.0, searching _MSC_VER 1900 in wiki that means Visual Studio 2015 (Update 2, if to be precise according to Microsoft article).

vs2015 can be compiled normally, because vs2015 can fully support the c++11 standard, you can consider upgrading your vs

Related

VC++ 2015 error: "an expression involving objects with internal linkage cannot be used as a non-type argument"

The following sample code does compiles in VC++ 2019, clang++ and g++ but not VC++ 2015.
namespace ns
{
template<const char* str>
struct Foo
{
};
static const char name[] = "Test";
Foo<name> foo;
}
int main()
{
}
Are there any workarounds for VC++ 2015? I'm assuming the code is conforming but VC++ 2015 had a bug which was fixed in VC++ 2019. I'd migrate to VC++ 2019 but my company builds in VC++ 2015.
MSVC 2015 doesn't fully support C++11, and non-type template arguments with internal linkage is an example of a C++11 feature which VC++ didn't support until version 14.14 (VS 2017 15.8).
I can think of 3 solutions:
Don't use template arguments with internal linkage by removing static const specifiers from char name[]
Upgrade the compiler to VC++ 14.20+ (MSVC 2019)
Use feature detection and/or conditional compilation
Regarding conditional compilation, it can be achieved like this:
#if !defined(_MSC_VER) || _MSC_VER > 1914 // this part can be in a global config.h
# define INTERNAL static const
#else
# define INTERNAL
#endif
namespace ns
{
template<const char* str>
struct Foo
{};
INTERNAL char name[] = "Test";
Foo<name> foo;
}
int main()
{
}
As mentioned in the comments, the issue lies with the fact that VS-2015 does not comply with the C++11 Standard, which is required to implement the code you have shown.
Looking at how the issue is described in this report, the only way I have found to resolve the issue is to remove both the static and const qualifiers in your declaration of name. The following compiles even with the VS-2010 build tools (I don't have access to the VS-2015 tools on my PC but, as this works with both VS-2010 and VS-2019, I assume it would also be OK in VS-2015).
namespace ns {
template<const char* str>
struct Foo {
};
char name[] = "Test"; // Adding either "static" or "const" causes C2970
Foo<name> foo;
}
int main()
{
}
Whether or not this workaround is suitable for your needs remains to be seen.

Function with auto parameter compiles using GCC but does not compile using Visual C++

Please considere the following code-snippet:
#include <iostream>
void print(auto arg) {
std::cout << arg << std::endl;
}
int main() {
print("Hi");
return 0;
}
As you can see here (https://godbolt.org/z/2GSrXs) using GCC the code compiles and runs fine.
As you can see here (https://godbolt.org/z/rtR6w9) using Visual C++ the code does not compile and results in the error message:
<source>(3): error C3533: a parameter cannot have a type that contains 'auto'.
It seems to me that this feature has not jet been implementet in Visual C++?
Additionally I was not able to find the compiler feature in the following list:
https://learn.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance?view=vs-2019. So I am not sure if the Microsoft developer have the feature on their ToDo List?
Functions receiving auto is a C++20 feature. It must be enabled with /std:latest. However, MSVC only has partial support for this syntax at this time, so not all declaration using this feature will work.
GCC has supported auto in function parameters since early C++14 days as an extension.
You can read more about this C++20 feature reading P1141R2: Yet another approach for constrained declarations
Remaining in C++14 standard, you can use the following lambda instead of your function:
auto print = [](auto arg) {
std::cout << arg << std::endl;
};

GCC 7 C++ 17 support for folding expressions

The following snippet will compile in GCC 8+, but fails to compile in GCC 7.
template <typename... THINGS>
struct A
{
explicit A(THINGS *... things)
{
(..., [thing = things](){}());
}
};
int main()
{
int thing;
const auto thingy = A{&thing};
}
godbolt
The stated failure is that the parameter pack isn't being expanded: parameter packs not expanded with '...'.
Checking the GCC standards compliance page, fold expressions should be supported in GCC 7.
Is there another flag i need besides std=c++17? (i didn't see one)
Is the standard not yet completely implemented? (i didn't see anything indicating that)
Can i make this work, or is this just a GCC 7 bug i'm going to have to work around?
This is a GCC bug, originally reported in version 8.01, fixed in version 8.2. It seems that the bug also occurs when fold-expressions are not used (the C++11-era "expander trick" mentioned by NathanOliver doesn't work either), so you'll have to use a longer workaround that doesn't require expanding a template parameter pack that is inside a lambda capture. For example:
template <typename THING>
void do_it(THING* thing) {
[thing]{}();
}
explicit A(THINGS *... things)
{
(..., do_it(things));
}
This is a GCC bug. Found the tracked issue, fixed in 8.2.

MSVC's (seem not perfect) support for constexpr

I am making the naive wheel of type traits's is_base_of. And Here's a minimal demo about my implementation(didn't consider robustness, is_class...).
#include <type_traits>
#include <cstdint>
struct A
{
};
struct B : A
{
};
template
<typename T, typename U>
struct IsBaseOf {
constexpr static bool Test(T* t)
{
return true;
}
constexpr static bool Test(...)
{
return false;
}
constexpr static bool value = IsBaseOf<T,U>::Test(static_cast<U*>(nullptr));
};
int main()
{
static_assert(IsBaseOf<A, B>::value, "Pass");
}
This demo can be compiled by gcc/clang but cannot be compiled by MSVC.
http://rextester.com/ATOC6638
http://rextester.com/IWU81465
When i type it on my laptop's Visual Studio 2015(with update patch 3). It cannot be compiled either, the IDE reminds me that "expression must have constant value" before compiling.
So I wonder how's MSVC support for constexpr, or is my code wrong?
This is almost certainly a bug in MSVC. Especially previous versions had numerous issues with constexpr. Here's just a bunch of them for example. Support for many new features is not all that great yet in MSVC. But it's getting better by the minute. You'll want to always use the latest version to try out this sort of stuff. VisualStudio 2017 compiles this code just fineā€¦
Your code compiles with Visual Studio 2017 (cl version 19.15.26726).
You could try adding /std:c++14 or /std:c++latest compiler switch.

Do Ideone and Codepad really not support C++03?

My question is related to Prasoon's question about non POD types and value initialization.
I tried the following code on online compilers like Ideone and Codepad but the executables gave runtime error on both the sites.
#include <iostream>
#include <cassert>
struct Struct {
std::string String;
int Int;
bool k;
};
struct InStruct:Struct
{
InStruct():Struct(){}
};
int main()
{
InStruct i;
assert ( i.Int == 0);
std::cout << "Hello";
}
Ideone Output here
Codepad Output here
Does that mean neither of them support C++03 value initialization feature?
Does that mean neither of them support C++03 value initialization feature?
Yes.
Prior to version 4.4, GCC did not completely support value initialization (the Boost GCC compatibility header explains this and has links to the relevant GCC defect reports; see line 77).
If your code needs to be portable, you should be very careful relying on value initialization; GCC did not support it fully until recently and Visual C++ does not fully support it even in its latest version, Visual C++ 2010.
The declaration
InStruct i;
does not invoke value initialization
$8.5.3/10 - "An object whose
initializer is an empty set of
parentheses, i.e., (), shall be
value-initialized."
If you want to value-initialize, you would require an expression something like
assert(InStruct().Int == 0);
Try it now! - Ideone supports GCC-4.5.1