Consider the following code:
#include <iostream>
struct FactoryTag
{
static struct Shape {} shape;
static struct Color {} color;
};
template <typename TFactory>
int factoryProducer(TFactory tag)
{
if constexpr (std::is_same<TFactory, FactoryTag::Shape>::value)
return 12;
else if constexpr (std::is_same<TFactory, FactoryTag::Color>::value)
return 1337;
}
int main()
{
std::cout << factoryProducer(FactoryTag::shape) << std::endl;
return 0;
}
It works fine with g++ -std=c++1z Main.cpp but in Visual Studio with MSVC set with c++17 support it gives
Error LNK2001 unresolved external symbol "public: static struct FactoryTag::Shape FactoryTag::shape" (?shape#FactoryTag##2UShape#1#A) StaticTest C:\Users\danielj\source\repos\StaticTest\StaticTest\StaticTest.obj 1
Is this a bug in MSVC?
Is this a bug in MSVC?
No, FactoryTag::shape is odr-used here, so it needs a definition (you're copy-constructing it, which goes through the implicitly generated copy constructor, which requires you to bind a reference). Nor is this a bug in gcc either, arguably, since there is no diagnostic required if a definition is missing.
The solution is to add a definition. The old way would be:
struct FactoryTag { ... };
Shape FactoryTag::shape{}; // somewhere in a source file
The new way would be:
struct FactoryTag {
struct Shape {} static constexpr shape {}; // implicitly inline in C++17
};
Related
Suppose I want to use coroutines with C++20 and restrict the promise type to accept only functions getting one argument of type int &. I write the following code:
#include <coroutine>
struct task {
struct promise_type {
promise_type(int &) {}
task get_return_object() { return {}; }
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
};
};
task my_task(int &) {
co_await std::suspend_never{};
}
int main() {
int x = 5;
my_task(x);
}
This compiles and works fine, both with GCC version 10+ and Visual Studio 2019 version 16.8+.
However, Visual Studio 2019 always complains that no default constructor exists for class "task::promise_type":
This error does not occur if I do not use a reference (e.g. int instead of int & as the argument type).
GCC does not show any warning or error, with and without the reference.
Am I doing something wrong?
Is this prohibited by the standard?
Or is it just a weird quirk of IntelliSense?
This seems to be a problem of IntelliSense and should work fine. To resolve the intellisense error, a constructor can be added just for IntelliSense:
#ifdef __INTELLISENSE__
promise_type();
#endif
The following code snippet compiles just fine with GCC 9.1 and Clang 6.0 under C++11/14/17 standards, but refuses to compile with Visual Studio 2019. I can change the return of getDummies() to auto and VS will compile with C++14/17 standards, but this breaks C++11 compatibility on all compilers, which I need to keep.
#include <cstdlib>
#include <utility>
template<typename T>
class Dummy
{
public:
//static auto getDummies() // Works but breaks C++11 compatibility
static std::pair<Dummy<int>, Dummy<int>> getDummies()
{
return std::make_pair(Dummy<int>{}, Dummy<int>{});
}
};
int main()
{
auto dummies = Dummy<int>::getDummies(); // Error C2079
return EXIT_SUCCESS;
}
The class Dummy is not fully defined at the point that the getDummies() function is defined, inside the class.
You can declare the function in the class then define it outside the class, like:
template<typename T>
class Dummy
{
public:
static std::pair<Dummy<int>, Dummy<int>> getDummies();
};
template<typename T>
std::pair<Dummy<int>, Dummy<int>> Dummy<T>::getDummies()
{
return std::make_pair(Dummy<int>{}, Dummy<int>{});
}
I'm trying to implement a simple system using template struct, the code is very simple and compile fine with MSVC, yet i cannot understand why CLANG gives me this error: "lld-link : error : undefined symbol: public: static struct FMyStruct const TSpec<1>::m_struct"
I compile on a windows 64bitmachine with VisualStudio IDE but CLANG LLVM as compiler. The code works fine with MSVC.
I simplified my problem to the very minimum, i tried to put everything in one single cpp file, with no result. I also tried explicit template instanciation.
I want to be compliant with C++14, no C++17. One thing i tried that worked was declaring the m_struct member as an inline variable, but then i get this warning: "inline variables are a C++17 extension"
struct FMyStruct
{
const int _p0;
const int _p1;
const int _p2;
};
template< int > struct TSpec {
static constexpr FMyStruct m_struct = { 0, 0, 0 };
};
FMyStruct
Function( int i )
{
return TSpec< 1 >::m_struct;
}
int main()
{
return 0;
}
Result:
"lld-link : error : undefined symbol: public: static struct FMyStruct const TSpec<1>::m_struct"
I expect the linker to find the symbol m_struct since it is defined very next to it ...
The weirdest part is that if i try:
int
Function( int i )
{
return TSpec< 1 >::m_struct._p0;
}
the program will compile fine.
Edit: My CLANG version is 9.0.0, prebuilt distributed version for windows from the official website.
clang version 9.0.0 (trunk)
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: C:\Program Files\LLVM\bin
It indeed seems to be a bug related to the CLANG version, thanks #Sombrero Chicken for pointing this out.
So this is definitely weird but i managed to solve this avoiding the C++17-specific 'inline' declaration of the static member by adding this after the template struct definition:
template< int N > const FMyStruct TSpec< N >::m_struct;
By the way, it does not seem to be related to the template declaration at all.
For summary, it gives this program that will compile fine.
struct FMyStruct
{
const int _p0;
const int _p1;
const int _p2;
};
template< int > struct TSpec {
static constexpr FMyStruct m_struct = { 0, 0, 0 };
};
template< int N > const FMyStruct TSpec< N >::m_struct;
FMyStruct
Function( int i )
{
return TSpec< 1 >::m_struct;
}
int main()
{
return 0;
}
I still do not really understand why this is necessary since the static member is public to the struct, and part of the same unit & file; i guess this is a different matter but i'd like to be enlightened. Thank you.
namespace MyNS {
template <>
class Test<Test1> {
public:
constexpr static char const *description[] = { "X`", "Y1"};
/*
...
...
*/
}
constexpr char const * Test<Test1>::description[];
/* Above definition is required when compiling with GCC but MSVC compiler gives error saying 'description' is redeclared. */
/* **Omitting definition of 'description', which is written outside class in namespace, causes successful compilation by MSVC but failure in GCC** */
}
Is there a common way to define, declare and initialize above constexpr such that code compiles successfully by both MSVC and GCC?
This code:
#include <iostream>
namespace MyNS {
template<class T> struct Test;
template <>
struct Test<int> {
constexpr static char const * description[] = { "X1", "Y1"};
};
}
int main() {
std::cout << MyNS::Test<int>::description[0];
return 0;
}
Compiles -as far as I can tell- using
g++-4.8+ -std=c++11
g++-4.8+ -std=c++1y
g++-4.9+ -std=c++14
g++-6.1+
g++-6.1+ -std=c++11
g++-6.1+ -std=c++14
without and further definition (where 4.8+ means starting from g++ version 4.8 and onwards).
I have a code in VC2010 which I've reduced to a small example.
Test.h:
#pragma once
template <typename TPixel>
struct Image
{
typedef TPixel PixelType;
};
template <typename TImageType>
struct Operation
{
void DoOperation()
{
ImageType::PixelType value = 0;
// I've done a misprint here. It should be TImageType::PixelType
}
};
Test.cpp:
void Test()
{
typedef Image<char> ImageType;
Operation<ImageType> op;
op.DoOperation();
}
As I expected, that produces an error.
test.h(14): error C2653: 'ImageType' : is not a class or namespace name
Now, let's change test.cpp just a little bit.
typedef Image<char> ImageType;
void Test()
{
Operation<ImageType> op;
op.DoOperation();
}
Now it compiles! Surprisingly enough, ImageType in DoOperation() now matches with the global typedef in test.cpp.
My question: why does it compile? Is that a Visual C++ bug or a standard behavior?
I take it that test.cpp includes test.h before the typedef, so it's actually
#include "test.h"
typedef Image<char> ImageType;
void Test()
{
Operation<ImageType> op;
op.DoOperation();
}
Completed like this, it is indeed a bug, or standard nonconforming behavior with regard to two phase lookup. Names that do not depend on a template parameter should be resolved relative to the point of declaration of the template.
I guess this is known behavior.