Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No type named 'unique_ptr' in namespace 'std' when compiling under LLVM/Clang

I'm catching a compile error when attempting to use unique_ptr on Apple platforms with -std=c++11:

$ make
c++ -std=c++11 -DNDEBUG -g2 -O3 -fPIC -march=native -Wall -Wextra -pipe -c 3way.cpp
In file included ...
./smartptr.h:23:27: error: no type named 'unique_ptr' in namespace 'std'
    using auto_ptr = std::unique_ptr<T>;
                     ~~~~~^
./smartptr.h:23:37: error: expected ';' after alias declaration
    using auto_ptr = std::unique_ptr<T>;

According to Marshall Clow, who I consider an expert on the C++ Standard Library with Clang and Apple:

Technical Report #1 (TR1) was a set of library additions to the C++03 standard. Representing the fact that they were not part of the "official" standard, they were placed in the namespace std::tr1.

In c++11, they are officially part of the standard, and live in the namespace std, just like vector and string. The include files no longer live in the "tr1" folder, either.

Take aways:

  • Apple and C++03 = use TR1 namespace
  • Apple and C++11 = use STD namespace
  • Use LIBCPP_VERSION to detect libc++

Now, here's what I have in smartptr.h:

#include <memory>

// Manage auto_ptr warnings and deprecation in C++11
// Microsoft added template aliases to VS2015
#if (__cplusplus >= 201103L) || (_MSC_VER >= 1900)
  template<typename T>
    using auto_ptr = std::unique_ptr<T>;
#else
  using std::auto_ptr;
#endif // C++11

I think the last thing to check is the __APPLE__ define, and here it is:

$ c++ -x c++ -dM -E - < /dev/null | grep -i apple
#define __APPLE_CC__ 6000
#define __APPLE__ 1
#define __VERSION__ "4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)"
#define __apple_build_version__ 5030040

Why am I receiving a error: no type named 'unique_ptr' in namespace 'std' when using -std=c++11?


I think these are the four test cases. It attempts to exercise the four configurations from the cross product of: {C++03,C++11} x {libc++,libstdc++}.

  • c++ -c test-clapple.cxx
    • OK
  • c++ -stdlib=libc++ -c test-clapple.cxx
    • OK
  • c++ -std=c++11 -c test-clapple.cxx
    • FAIL
  • c++ -std=c++11 -stdlib=libc++ -c test-clapple.cxx
    • OK

Here is the test driver. Be sure to test it on OS X so you get the full effects of the TR1 namespace in 2015.

$ cat test-clapple.cxx

// c++ -c test-clapple.cxx
// c++ -stdlib=libc++ -c test-clapple.cxx
// c++ -std=c++11 -c test-clapple.cxx
// c++ -std=c++11 -stdlib=libc++ -c test-clapple.cxx

#include <memory>

// Manage auto_ptr warnings and deprecation in C++11
#if (__cplusplus >= 201103L) || (_MSC_VER >= 1900)
  template<typename T>
    using auto_ptr = std::unique_ptr<T>;
#else
    using std::auto_ptr;
#endif // C++11

int main(int argc, char* argv[])
{
    return argc;
}
like image 922
jww Avatar asked Jul 27 '15 14:07

jww


1 Answers

And the CFE Devs specifically told me to use that code;

No they didn't. They told you to do something similar if you want to use shared_ptr, because for C++03 <tr1/memory> defines std::tr1::shared_ptr and for C++11 <memory> defines std::shared_ptr.

But you're not using shared_ptr. If you want to use auto_ptr then it's just std::auto_ptr, everywhere, which is always defined in <memory>.

I think you've misunderstood Marshall's comment and you're overcomplicating things. What you quoted ('In c++11, they are officially part of the standard, and live in the namespace std, just like vector and string. The include files no longer live in the "tr1" folder, either.') is not Apple-specific or Clang-specific, it applies to all compilers. But since auto_ptr was never part of TR1 and never in <tr1/memory> it's irrelevant that the contents of TR1 are now in namespace std, because what you're trying to use was never included in TR1.

You should not be using TR1 at all here.

# include <memory>

// Manage auto_ptr warnings and deprecation in C++11
#if (__cplusplus >= 201103L)
template<typename T>
using auto_ptr = std::unique_ptr<T>;
#else
using std::auto_ptr;
#endif // C++11

This should be correct for modern compilers, but won't work on the stupid configuration that comes with XCode, which is a modern version of Clang that supports C++11 and the libstdc++ from GCC 4.2 which is nearly ten years old and doesn't support unique_ptr.

To cope with the default OS X toolchain this works:

#include <memory>

#if __cplusplus >= 201103L
# ifdef __clang__
#  if __has_include(<forward_list>)
// either using libc++ or a libstdc++ that's new enough to have unique_ptr
#   define HAVE_UNIQUE_PTR 1
#  endif
# else // not clang, assume unique_ptr available
#  define HAVE_UNIQUE_PTR 1
# endif
#endif

#ifdef HAVE_UNIQUE_PTR
template<typename T> using auto_ptr = std::unique_ptr<T>;
#else
using std::auto_ptr;
#endif

This works by using the presence of <forward_list> as an indicator of whether the standard library clang is using supports std::unique_ptr.

If clang is using libc++ as its standard library then all versions support unique_ptr and also provide <forward_list>, so the test passes.

If clang is using libstdc++ then whether unique_ptr is supported depends on the libstdc++ version. unique_ptr was added to libstdc++ in GCC 4.3, which is the same version that added <forward_list>, so if that header is available then unique_ptr will be too. If you are using clang with the ancient libstdc++ that ships with the Apple toolchains (from GCC 4.2) then unique_ptr is not supported, but neither is <forward_list>, so the test fails and you use auto_ptr instead.

That should work for any GCC/libstdc++, Clang/libc++ or Clang/libstdc++ combination found in the wild. I don't know what is needed for VC++/Dinkumware and Clang/Dinkumware, from your answer it looks like maybe you would just change the first condition to:

#if __cplusplus >= 201103L || _MSC_VER >= 1600
like image 183
Jonathan Wakely Avatar answered Oct 11 '22 14:10

Jonathan Wakely