Different compiler behaviour when using alias as scope to get parent member - c++

This code compiles fine on Clang and Visual C++ but not on GCC:
#include <iostream>
template <class T>
struct Test {
Test(T &t) : _t(t) {
}
void method() {
std::cout << _t.Internal::_value << "\n"; // Doesn't work on GCC
std::cout << _t.T::Internal::_value << "\n"; // Work on all compilers
}
private:
T &_t;
};
template <class T>
struct Base {
T _value = 1;
};
template <class T>
struct Child : Base<int> {
using Internal = Base<int>;
int _value = 2;
};
int main(int argc, const char * argv[]) {
Child<float> child;
Test<Child<float>> test(child);
test.method();
return 0;
}
The error message from GCC is
error: 'Internal' has not been declared
9 | std::cout << _t.Internal::_value << "\n";
| ^~~~~~~~
Which one is right ?

Visual C++ and Clang are right in accepting this code.
It was a bug in GCC that prevented it from doing the same. The error as in the question was up to GCC 10, in GCC 11 its wording changed to
error: request for member 'Internal' in non-class type 'T'
9 | std::cout << _t.Internal::_value << "\n";
| ^~~~~~~~
And GCC trunk finally accepts the code as well. Demo: https://gcc.godbolt.org/z/dj34Yhns3
So we could expect the fix in GCC 12.

Related

Emscripten: how to disable warning: explicit specialization cannot have a storage class

I am building my program by using the latest Emscripten compiler.
It is based on Clang version 14. Actually it is a small test program which is the following:
#include <iostream>
struct Test {
template<typename T>
static inline void Dump(const T& value) {
std::cout << "[generic] = '" << value << "'\n";
}
template<>
static inline void Dump<std::string>(const std::string& value) {
std::cout << "[std::string] = '" << value << "'\n";
}
};
int main() {
std::string text = "hello";
Test::Dump(text);
return 0;
}
When I build it by Emscripten compiler I got the warning:
D:\em_test>emcc a.cpp
a.cpp:10:24: warning: explicit specialization cannot have a storage class
static inline void Dump<std::string>(const std::string& value) {
~~~~~~~ ^
1 warning generated.
If I just remove static keyword from void Dump<std::string> line
then there will be no warning. However, this code will cause compilation error in Visual Studio:
D:\em_test\a.cpp(17,11): error C2352: 'Test::Dump': illegal call of non-static member function
But this error is expected and clear.
I would like to write a cross-platform program.
So, I think I should simple disable this warning in Emscripten.
However, I can not find any Emscripten (which is based on clang version 14)
command line option for that!
And I am asking advice for that.
Actually I tried to use -Wno-static-inline-explicit-instantiation command line option but it did not help:
D:\em_test>emcc -Wno-static-inline-explicit-instantiation a.cpp
a.cpp:10:24: warning: explicit specialization cannot have a storage class
static inline void Dump<std::string>(const std::string& value) {
~~~~~~~ ^
1 warning generated.
However, I see in Clang version 13 user manual description about -Wstatic-inline-explicit-instantiation option but it is about a slightly another warning text.
Also it seems that Clang version 14 is not fully released, so, there is no public Clang version 14 user manual.
I can not find any Emscripten or Clang command line option to disable the above warning.
Could somebody help me?
Explicit specialization of (both static and non-static) function templates cannot be put into class definitions.
Just put it into the enclosing namespace(i.e somewhere after the class):
#include <iostream>
struct Test {
template <typename T>
static inline void Dump(const T& value) {
std::cout << "[generic] = '" << value << "'\n";
}
};
// Notice Test::
template <>
inline void Test::Dump<std::string>(const std::string& value) {
std::cout << "[std::string] = '" << value << "'\n";
}
int main() {
std::string text = "hello";
Test::Dump(text);
return 0;
}
inline is never necessary for in-class function definitions but it has different meaning for member variables.
inline for out-class is necessary in header files because the explicit specialization is not a template anymore.

Problem using boost::object_pool boost version 1.56 with visual studio 2019

I am trying to convert boost::object_pool usage on my old project to new visual studio 2019 project, I am using boost version 1.56
ObjectPool.h
class BOOST_OBJECT_POOL_CHECKER
{
boost::object_pool< T > m_sObjectPool;
template <class Arg1>
T* contruct(Arg1& sArg1)
{
T* temp = m_sObjectPool.construct(sArg1);
return temp;
}
}
MaterialServer.h
class MaterialServer
{
MaterialServer(dword serviceType, std::string path);
Material* NEW_MATERIAL();
}
Material.h
class Material
{
BOOST_OBJECT_POOL_CHECKER<Material> m_poolMATERIAL;
Material(MaterialServer* pMatServer);
}
Material.cpp
Material* MaterialServer::NEW_MATERIAL()
{
//Material* returnMaterial = m_poolMATERIAL.construct(this); << error on vs2019, not correct parameter
Material* returnMaterial = m_poolMATERIAL.construct(*this);
}
got first error
boost_1_56\boost\pool\detail\pool_construct_simple.ipp(19,1): error C2664: 'Material::Material(MaterialServer*)': cannot convert argument 1 from 'const T0' to 'MaterialServer *'
ObjectPool.h(68): message : see reference to function template instantiation 'Material *boost::object_pool<T,boost::default_user_allocator_new_delete>::construct<Arg1>(const T0 &)' being compiled
with
[
T=Material,
Arg1=MaterialServer,
T0=MaterialServer
]
should I need upgrade boost version? because previously this code compiled fine on vs2008, but not compiled on vs2019, this c++11 standard so confusing for me
can I get explanation this behavior?
Frankly, this code cannot have compiled under any compiler.
Note: I'm ignoring numerous typos, omitted semi-colons, omitted template declarators, typedefs and access specifiers to focus on the real issues.
You're passing *this which is Material&. However, the contruct [sic] function takes a MaterialServer*.
So, in fact, the commented line was closer, and makes sense IFF it were a member of MaterialServer, not Material.
It would make a lot more sense, logically, for the material server to "create new materials", anyways, and almost works:
class Material {
public:
Material(MaterialServer* pMatServer);
};
class MaterialServer {
BOOST_OBJECT_POOL_CHECKER<Material> m_poolMATERIAL;
public:
MaterialServer(dword serviceType, std::string path);
Material* NEW_MATERIAL();
};
Material* MaterialServer::NEW_MATERIAL()
{
Material* returnMaterial = m_poolMATERIAL.construct(this);
return returnMaterial;
}
I say /almost/ because construct takes its argument by mutable reference. That won't compile here (this is NOT a mutable lvalue).
So, fixing that:
template <typename Arg1> T* construct(Arg1 sArg1) {
return m_sObjectPool.construct(sArg1);
}
Or, more generically:
template <typename... Arg> T* construct(Arg&&... sArg) {
return m_sObjectPool.construct(std::forward<Arg>(sArg)...);
}
We get "compiling code". We can't link it (the constructors aren't defined).
Adding some more imagined code:
Live On Coliru
#include <boost/pool/object_pool.hpp>
#include <iomanip>
#include <iostream>
#include <string>
#include <atomic>
using dword = uint32_t;
template <typename T> class BOOST_OBJECT_POOL_CHECKER {
boost::object_pool<T> m_sObjectPool;
public:
template <typename... Arg> T* construct(Arg&&... sArg)
{
return m_sObjectPool.construct(std::forward<Arg>(sArg)...);
}
};
class MaterialServer; // forward declare
class Material {
public:
Material(MaterialServer* pMatServer);
};
class MaterialServer {
BOOST_OBJECT_POOL_CHECKER<Material> m_poolMATERIAL;
dword _serviceType;
std::string _path;
public:
MaterialServer(dword serviceType, std::string path)
: _serviceType(serviceType)
, _path(path)
{
}
Material* NEW_MATERIAL();
dword getServiceType() const { return _serviceType; }
std::string_view getPath() const { return _path; }
};
Material* MaterialServer::NEW_MATERIAL()
{
Material* returnMaterial = m_poolMATERIAL.construct(this);
return returnMaterial;
}
Material::Material(MaterialServer* pMatServer)
{
static std::atomic_int id{0};
std::cout << "Material " << id++ << " from server ("
<< pMatServer->getServiceType() << ", "
<< std::quoted(pMatServer->getPath()) << ")\n";
}
int main() {
MaterialServer a(123, "Material/a/resource");
MaterialServer b(234, "Material/b/resource");
a.NEW_MATERIAL();
a.NEW_MATERIAL();
b.NEW_MATERIAL();
a.NEW_MATERIAL();
}
Prints
Material 0 from server (123, "Material/a/resource")
Material 1 from server (123, "Material/a/resource")
Material 2 from server (234, "Material/b/resource")
Material 3 from server (123, "Material/a/resource")

undefined reference to variable template of pointer to function in clang but not gcc

#include <iostream>
static constexpr bool isSSE2 = true;
template<typename T>
static void (*fp)();
template<typename T>
static void foo_c() {
std::cout << "foo_c get called." << std::endl;
}
template<typename T>
static void foo_sse2() {
std::cout << "foo_sse2 get called." << std::endl;
}
int main() {
if (isSSE2)
fp<int> = foo_sse2<int>;
else
fp<int> = foo_c<int>;
fp<int>();
return 0;
}
I have a project that uses variable template which itself is a pointer to function. The example code above compiles and executes fine in GCC 6.3, but gives warning and error in clang 3.9.1.
$ clang++ "Source.cpp" -o "foo.exe" -std=c++14 -O2
Source.cpp:6:15: warning: variable 'fp<int>' has internal linkage but is not defined [-Wundefined-internal]
static void (*fp)();
^
Source.cpp:20:9: note: used here
fp<int> = foo_sse2<int>;
^
1 warning generated.
C:\msys64\tmp\Source-6600e8.o:(.text+0x2a): undefined reference to `fp<int>'
clang++.exe: error: linker command failed with exit code 1 (use -v to see invocation)
Any help is appreciated.
You have to initialize your fp<>() first:
template<typename T>
static void (*fp)() = nullptr;
It compiles and runs fine in Clang 4.0: sample code.
And try to always initialize your variables — it may save you from all sorts of headaches. :)

Calling non template member function inside template member function

I am facing compilation problem while running the following program:
I am calling non template member function inside template member function but getting the weird compilation errors.
#include <iostream>
#include <boost\shared_ptr.hpp>
class base
{
public:
base()
{
}
void fun2(boost::shared_ptr<int> &data)
{
std::cout << "This is fun2" << std::endl;
}
void fun3(boost::shared_ptr<double> &value)
{
std::cout << "This is fun3" << std::endl;
}
template <typename T>
void fun1(int switchParam,T &resonse)
{
std::cout << "This is fun1." << std::endl;
switch(switchParam)
{
case 0:
fun2(resonse);
break;
case 1:
fun3(resonse);
break;
}
}
};
void main()
{
boost::shared_ptr<int> myInt;
int switchParam = 0;
base b1;
b1.fun1(switchParam,myInt);
}
Getting the following compilation problem:
Error 1 error C2664: 'base::fun3' : cannot convert parameter 1 from 'boost::shared_ptr<T>' to 'boost::shared_ptr<T> &'
Any help will be appreciated.
No. You cannot do that. With second phase of template code compilation for any type the switch block has to be fully compiled by compiler. It will will fail to compile. You are mixing templates with runtime behavior of program. You better write a different function.
Note that switch is runtime, not compile time. When you call it as fun1(0) the compiler still has to compile it fully for int. It won't evaluate runtime switch statement and eliminate fun3, which takes shared_ptr<double>.

compilation error in C++

I have the following code in C++ and I got the compilation error:
a.cpp: In member function `virtual void Derived<T, D>::run(T&)':
a.cpp:13: error: expected primary-expression before "int"
a.cpp:13: error: expected `;' before "int"
Please help me find out what went wrong here. Thanks a lot.
#include <iostream>
template<typename T> struct Base
{
virtual void run( T& ){}
virtual ~Base(){}
};
template<typename T, typename D> struct Derived : public Base<T>
{
virtual void run( T& t )
{
D d;
d.operator()<int>();//nor does d.operator()<T>(); work
}
};
template<typename T> struct X
{
template<typename R> X(const R& r)
{
std::cout << "X(R)" << std::endl;
ptr = new Derived<T,R>();
}
X():ptr(0)
{
std::cout << "X()" << std::endl;
}
~X()
{
if(ptr)
{
ptr->run(data);
delete ptr;
}
else
{
std::cout << "no ptr" << std::endl;
}
}
Base<T>* ptr;
T data;
};
struct writer
{
template<typename T> void operator()()
{
std::cout << "T "<< std::endl;
}
};
int main()
{
{
writer w;
X<int> xi1((writer()));
}
return 0;
};
In Derived<>::run(), change
d.operator()<int>();
to
d.template operator()<int>();
For further information, see this FAQ:
What is the ->template, .template and ::template syntax about?
Your original version works when compiled with Microsoft C++ Compiler version 15.00.21022.08 that comes with Visual Studio 2008 with the following message:
C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\xlocale(342) :
warning C 4530: C++ exception handler used, but unwind semantics are not enabled.
Specify /EHsc
Microsoft (R) Incremental Linker Version 9.00.21022.08 Copyright (C) Microsoft Corporation. All rights reserved.
/out:a.exe a.obj