msvc inline static member variable redefinition - c++

I'm new to C++17. Considering the following code:
// ---------------
// in MyClass.hpp
// ---------------
#pragma once
class MyClass {
public:
static const int A;
};
inline const int MyClass::A = 100;
// ---------------
// in test.cpp
// ---------------
#include <stdio.h>
#include "MyClass.hpp"
void test() {
printf("test: %p\n", &MyClass::A);
}
// ---------------
// in main.cpp
// ---------------
#include <stdio.h>
#include "MyClass.hpp"
extern void test();
int main() {
printf("main: %p\n", &MyClass::A);
test();
}
When compiled with MinGW-W64 g++ 8.1.0
g++ -std=c++17 main.cpp test.cpp -o test.exe
The output is
main: 00000000004044B0
test: 00000000004044B0
which works as expected.
However, in MSVC 2017
cl /std:c++17 main.cpp test.cpp
I got a compiler error, saying redefinition of "public: static int const MyClass::A". (Sorry, the compiler output contains Chinese characters. It's not appropriate to post here directly.)
Why the code works under g++, but fails in MSVC? Did I do something wrong?

I can confirm that Clang accepts your code without any warning.
What worries me is that cppreference shows the following note:
The inline specifier cannot re-declare a function or variable (since C++17) that was already defined in the translation unit as non-inline.
I could not really identify the real cause for that note in C++ standard. But as cppreference is generally correct in its warnings, I assume that it is the reason why MSVC chokes on your code. It would probably expect:
// ---------------
// in MyClass.hpp
// ---------------
#pragma once
class MyClass {
public:
static const int A = 100;
};
in order to avoid a former non inline declaration followed with an inline definition.

Related

C++ clang compiling & linking on ipad, Linker issue

please may you advise how I may compile & run main.cpp while compiling and linking the my_class.cpp & my_class.h class files,please note that this is running on an iPad using the “Code” app by “thebaselab”, which has offline clang++ 13.0. developer says its possible to work using the below method, however theres no output.
I compile seperately:
clang++ main.cpp -c
clang++ my_class.cpp - c
It seems to produce the main.o and my_class.o files so I may run using:
clang++ main.o my_class.o
This doesnt seem to run as no output, please may you advise if you can see the problem in my code or in compiling.?
I believe there is an issue with linking these files together, as when I have the class defined in main there’s no issues.
My code base:
main.cpp
// Created on iPad.
#include <iostream>
#include <vector>
#include "my_class.h"
using namespace std;
int main() {
cout << "Hey\n";
my_class obj1 = my_class("test");
obj1.display();
cout << "Hello World!";
return 0;
}
my_class.h
#ifndef _my_class_H_
#define _my_class_H_
class my_class
{
private:
std::string name = "";
public:
my_class(std::string name_tmp); // No-args constructor // Copy constructor
~my_class(); // Destructor
void display();
};
#endif
my_class.cpp
#include <iostream>
#include "my_class.h"
// 1-args constructor
my_class::my_class(std::string name_tmp){
name = name_tmp;
}
// Destructor
my_class::~my_class() {
std::cout << "Destructing\n";
}
void my_class::display(){
std::cout << name << "\n";
}
Thanks to #Quimby,
I ran clang++ -o a.out main.o my_class.o then just a.out and it works on my iPad.

Instance of class shared between static libraries (obj files)

I have a C++ application with the following 3 files:
// sample.h
#ifndef sample_h
#define sample_h
#include <stdio.h>
namespace mynamespace {
class sample {
public:
void myprintf(const char* tmp);
};
}
#endif
// sample.cpp
#include "sample.h"
void mynamespace::sample::myprintf(const char* val) {
printf(val);
}
// main.cpp
#include "sample.h"
int main() {
mynamespace::sample sample1; // How to omit this line?
sample1.myprintf("Hello world!");
}
Is it possible to remove the instantiation of sample1 object from main.cpp, and to have it already available (coming from the static library "sample.obj")?
If I move that line to sample.h, then the error I get during compilation is:
"class mynamespace::sample sample1" already defined in sample.obj
If I move that line to sample.cpp, then the error message is:
'sample1': undeclared identifier
Actually I understand why both errors occur, I just don't know what is the solution.
Thanks
Use static declaration:
in sample.h
namespace mynamespace{
class sample {
public:
static sample sample1;
void myprintf(const char* tmp);
};
static sample& sample1 = sample::sample1;
}
then in sample.cpp
mynamespace::sample mynamespace::sample::sample1;
from main.cpp
access the variable
mynamespace::sample::sample1.myprintf("");
mynamespace::sample1.myprintf("");
Specify an extern storage class in the header file:
namespace mynamespace {
// ...
extern sample sample1;
}
And then define it normally in sample.cpp:
mynamespace::sample sample1;
It's possible that on some compilers/operating systems it will be necessary to specify something else, something that's compiler-specific, in order to get external linkage to a data symbol in a library. This is beyond the scope of the C++ standard. Consult your compiler's documentation for more information.

Multiple definition gets solved with templates

a.hpp:
#pragma once
struct S
{
static int v;
};
int S::v = 0;
b.hpp:
#pragma once
void addOne();
b.cpp:
#include "b.hpp"
#include "a.hpp"
void addOne()
{
S::v += 1;
}
main.cpp:
#include <iostream>
#include "a.hpp"
#include "b.hpp"
int main()
{
S::v = 2;
addOne();
S::v += 2;
std::cout << S::v << std::endl;
}
Does not work when compiling with g++ -std=c++14 main.cpp b.cpp && ./a.out (multiple definition of S::v).
However when I change the code to:
a.hpp:
#pragma once
struct S
{
template<typename T>
static int v;
};
template<typename T>
int S::v = 0;
and replace all S::v with S::v<void> it compiles and works how I intended the first example to work (outputs 5).
I believe I know why the first code example does not work: The int S::v = 0; line gets once compiled in the main.cpp unit and once in the b.cpp unit. When the linker links these two together, then the variable S::v gets essentially redefined.(?)
Why does the code with the template work?
Why does the code with the template work?
Essentially, because the standard says so.
With templates, the rules usually amoun to: "everyone using them must have their definition available." The exact same applies to static data members of class templates: a definition of such a static data member must be present in every translation unit in which it is odr-used. It's up to the compiler & linker to make sure this does not lead to errors.
Note that since C++17, you can solve the non-template case by making the static data member inline:
#pragma once
struct S
{
static inline int v = 0;
};

Avoiding duplicate symbol due to initialization of specialization in header?

I'm catching duplicate symbol errors due to definitions I am trying to provide in a header. Here's the error from the Minimal, Complete, and Verifiable example. The header files and source files are shown below.
$ clang++ main.cpp x.cpp y.cpp -o main.exe 2>&1 | c++filt
duplicate symbol Id<S>::id in:
/tmp/main-3f2415.o
/tmp/long long-d62c28.o
duplicate symbol Id<T>::id in:
/tmp/main-3f2415.o
/tmp/long long-d62c28.o
duplicate symbol Id<S>::id in:
/tmp/main-3f2415.o
/tmp/unsigned long long-bfa6de.o
duplicate symbol Id<T>::id in:
/tmp/main-3f2415.o
/tmp/unsigned long long-bfa6de.o
ld: 4 duplicate symbols for architecture x86_64
Here is a similar question, but it does not involve a specialization: Static member initialization in a class template. This question has the specialization but it is for MSVC and not Clang: How to initialize a static member of a parametrized-template class. And this question states to put it in the source (*.cpp) file but we are aiming for the header file to avoid Clang 3.8 and 'Id<S>::id' required here, but no definition is available warnings: Where should the definition of an explicit specialization of a class template be placed in C++?
GCC, ICC, MSVC, SunCC and XLC are OK with the initialization. Clang and LLVM is giving me the trouble. Clang and LLVM also has trouble with explicit instantiations of specializations and extern, so its it own special kind of hell.
We support C++03 though C++17, so we have to be careful of the solution. Naively I tried placing the initialization of the specializations in an unnamed namespace to keep the symbols from escaping the translation units, but it resulted in compile errors.
How do we avoid duplicate symbol definitions when initializing and specializing a template class in a header?
Below is the MCVE, which is a cat main.cpp a.h s.h s.cpp t.h t.cpp x.cpp y.cpp. The problem seems to be with a.h, which provides the specialization and initialization; and the source files x.cpp and y.cpp, which include a.h.
main.cpp
#include "a.h"
#include "s.h"
#include "t.h"
int main(int argc, char* argv[])
{
uint8_t s = Id<S>::id;
uint8_t t = Id<T>::id;
return 0;
}
a.h
#ifndef A_INCLUDED
#define A_INCLUDED
#include <stdint.h>
template <class T> class Id
{
public:
static const uint8_t id;
};
class S;
class T;
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
#endif
s.h
#ifndef S_INCLUDED
#define S_INCLUDED
class S {
public:
S();
};
#endif
s.cpp
#include "s.h"
S::S() {}
t.h
#ifndef T_INCLUDED
#define T_INCLUDED
class T {
public:
T();
};
#endif
t.cpp
#include "t.h"
T::T() {}
x.cpp
#include "a.h"
y.cpp
#include "a.h"
Clang/LLVM is not the problem. You're simply running into undefined behavior with a touch of no diagnostic required. The fix is simple. You need to put your specialization(s) in one translation unit. i.e,
a.cpp:
#include "a.h"
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
Then the command line:
clang++ main.cpp a.cpp x.cpp y.cpp -o main.exe 2>&1 | c++filt
And voila. It works.
You are violating the ODR (one definition rule) for Id::id and Id::id. They get defined for each translation unit they are included in and thus show up when you link.
Depending on your intent with the id's for class S and T, you have to give them a unique home. One might be to park them in main.cpp.
main.cpp add
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
Or, put the id of S in s.cpp and id of T in t.cpp:
s.cpp
#include "s.h"
#include "a.h"
template<> const uint8_t Id<S>::id = 0x01;
and the equivalent for t.cpp.
Don't forget to remove any traces of S and T in a.h.
But, if they are part of the a.h interface, the create an a.cpp and define them there.
You can use unnamed namespace which can make your class have internal linkage, e.g.
#ifndef A_INCLUDED
#define A_INCLUDED
#include <stdint.h>
namespace {
template <class T> class Id
{
public:
static const uint8_t id;
};
}
class S;
class T;
template<> const uint8_t Id<S>::id = 0x01;
template<> const uint8_t Id<T>::id = 0x02;
#endif
Replace this code with the contents in a.h in your example, then it will work because Id<T> in one translation unit is different from that in another translation unit due to internal linkage, thus one-definition rule is not violated.

in-class non-static member initialization via std::is_convertible fails in gcc

I compiled that with gcc version 4.9.2 (Debian 4.9.2-10), but it gives me an seemingly unrelated error.
is that possibly a compiler bug?
#include <type_traits>
#include <vector>
#include <iostream>
#include <iterator>
struct test_class {
bool B1 = std::is_convertible<std::output_iterator_tag, std::output_iterator_tag>::value; // error: invalid use of ‘::’
//static const bool B1 = std::is_convertible<std::output_iterator_tag, std::output_iterator_tag>::value; // works fine
//bool B1;
//test_class() : B1(std::is_convertible<std::output_iterator_tag, std::output_iterator_tag>::value) {} // works fine
};
int main() {
test_class a;
std::cout << a.B1;
}
compiled via: name#machine:~$ g++ -std=c++14 example.cpp
MSVC 2015 is compiling just fine.