While trying to compile some CUDA code using Intel Threading Building Blocks, I discovered what I think is a bug in nvcc
. The following minimal example compiles fine using g++
5.4:
class Sub;
class Other;
namespace internal {
class Base
{
private:
friend class ::Sub;
static void foo(::Sub& b);
static void foo(::Other& c);
};
}
class Sub : private internal::Base
{
public:
using internal::Base::foo;
};
void internal::Base::foo(Sub& b)
{
}
int main(int argc, char *argv[])
{
Sub *b;
b->foo(*b);
// Sub::foo(*b);
return 0;
}
But if I compile it with nvcc
8.0 combined with the same host compiler, using
nvcc -x cu -arch=sm_35 -c minimal.cc
I get the following interesting error:
../minimal.cc: In function ‘int main(int, char**)’:
../minimal.cc:28:21: error: ‘internal::Base’ is an inaccessible base of ‘Sub’
A somewhat more descriptive error is obtained if Base
is moved out of the internal
namespace and into to global namespace:
../minimal.cc: In function ‘int main(int, char**)’:
../minimal.cc:6:12: error: ‘class Base Base::Base’ is inaccessible
class Base
^
../minimal.cc:32:5: error: within this context
b->foo(*b);
^
../minimal.cc:32:11: error: ‘Base’ is an inaccessible base of ‘Sub’
b->foo(*b);
Clearly, this seem to be due to the somewhat non-standard way of calling the static method using a pointer, and if that row is replaced by the one commented out, it compiles just fine.
Can someone confirm if this is valid C++ and thus a bug in nvcc
, or invalid C++ that g++
somehow still happily accepts?
I dug some more into this, and saw that it indeed is a problem with one of the phases of the
nvcc
compilation. Using--save-temps
, and checking out the resulting.cu.cpp.ii
file, it turns out that this linegets substituted by the following
This does not compile with
g++
, since then the fact thatfoo
is exported as public inSub
is lost. After all, this tries to explicitly access it from the base class where it isprivate
. Using the other type of invocation (Sub::foo
) does not result in any extra code being generated.I conclude that this is an bug in
nvcc
. It is interesting that this substitution does not happen if the second overloadedvoid foo(::Other &c)
is not declared inBase
.