与匿名命名空间的C ++内部链接:这真的和它一样好吗?

时间:2013-07-13 03:16:34

标签: c++11 instantiation linkage explicit-instantiation

在我的班级Foo中,我需要使用辅助函数构造一个对象,并且 - 作为帮助强制代码结构的任意第二个函数 - 设置一个处理程序。

由于内部联系而露出定义似乎是唯一的方法,但它冒犯了我的风格,我希望我忽略了另一种方式来解决这个问题。这是一个完整的例子,说明了这一点(包括makefile!):

// ideal_foo.hpp
#ifndef IDEAL_FOO_HPP
#define IDEAL_FOO_HPP

#include <functional>
#include <memory>
#include <string>

namespace ideal {

template <typename T>
class Foo : public std::enable_shared_from_this< Foo<T> > {
 public:
  using FooType = Foo<T>;
  using HandlerFunc = std::function<void(const std::string&)>;
  using Ptr = std::shared_ptr<FooType>;

  static Ptr Init();
  ~Foo() = default;
  void setHandler(HandlerFunc func);

 private:
  Foo();
  class Impl;
  std::unique_ptr<Impl> impl_;
};

} // namespace ideal
#endif // IDEAL_FOO_HPP
// ideal_foo.cpp
#include "ideal_foo.hpp"

namespace ideal {

template<typename T>
class Foo<T>::Impl {
 public:
  HandlerFunc func_ = nullptr;
};

template<typename T>
typename Foo<T>::Ptr
Foo<T>::Init() {
  return std::make_shared<Foo>();
}

template<typename T>
Foo<T>::Foo() : impl_{new Impl{}} { }

template<typename T>
    void
Foo<T>::setHandler(HandlerFunc func) {
  impl_->func_ = func;
}

} // namespace ideal

哪个编译,一切都很好,直到我尝试在外部使用它。

// ideal_bar.cpp
#include "ideal_foo.hpp"

namespace ideal {
namespace {

class Bar {
 public:
  using FooType = Foo<Bar>;
  Bar() : foo_{FooType::Init()} {}

  FooType::Ptr foo_;
};

} // unnamed-namespace
} // namespace ideal


int
main() {
  ideal::Bar b;
}

当我尝试在没有ideal_bar.cpp的情况下编译-Werror时,我收到以下错误:

clang++ -std=c++11 -stdlib=libc++ -Wall -pedantic -o ideal ideal_bar.o ideal_foo.o
Undefined symbols for architecture x86_64:
  "ideal::Foo<ideal::(anonymous namespace)::Bar>::Init()", referenced from:
      ideal::(anonymous namespace)::Bar::Bar() in ideal_bar.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [ideal] Error 1

-Werror,它抓住了这个的根源:

clang++ -std=c++11 -stdlib=libc++ -Wall -Werror -pedantic -c -o ideal_foo.o ideal_foo.cpp
clang++ -std=c++11 -stdlib=libc++ -Wall -Werror -pedantic -o ideal ideal_bar.o ideal_foo.o
Undefined symbols for architecture x86_64:
  "ideal::Foo<ideal::(anonymous namespace)::Bar>::Init()", referenced from:
      ideal::(anonymous namespace)::Bar::Bar() in ideal_bar.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [ideal] Error 1

问题:唯一的办法是避免添加foo_impl.hpp,从而将Init()的定义以及Impl中的任何其他必需项目展示给{ {1}}?以下是实践中的一个实例:

bar.cpp

然后回到// fugly_foo.hpp #ifndef FUGLY_FOO_HPP #define FUGLY_FOO_HPP #include <functional> #include <memory> #include <string> namespace fugly { template <typename T> class Foo : public std::enable_shared_from_this< Foo<T> > { public: using FooType = Foo<T>; using HandlerFunc = std::function<void(const std::string&)>; using Ptr = std::shared_ptr<FooType>; static Ptr Init(); ~Foo() = default; void setHandler(HandlerFunc func); private: Foo(); class Impl; std::unique_ptr<Impl> impl_; }; } // namespace fugly #endif // FUGLY_FOO_HPP fugly_bar.cpp,如:

#include "foo_impl.hpp"

为了完整性,这里是// fugly_bar.cpp - bar.cpp, the fugly revised edition #include "fugly_foo.hpp" namespace fugly { namespace { class Bar; } // unnamed-namespace } // namespace fugly // FUGLY: Is this really the only way to get this to work? #include "fugly_foo_impl.hpp" namespace fugly { namespace { class Bar { public: using FooType = Foo<Bar>; Bar() : foo_{FooType::Init()} {} FooType::Ptr foo_; }; } // unnamed-namespace } // namespace fugly int main() { fugly::Bar b; }

fugly_foo.cpp

// fugly_foo.cpp #include "fugly_foo.hpp" namespace fugly { template<typename T> class Foo<T>::Impl { public: HandlerFunc func_ = nullptr; }; template<typename T> typename Foo<T>::Ptr Foo<T>::Init() { return std::make_shared<Foo>(); } template<typename T> void Foo<T>::setHandler(HandlerFunc func) { impl_->func_ = func; } } // namespace fugly

fugly_foo_impl.hpp

让事情变得简单,#ifndef FUGLY_FOO_IMPL_HPP #define FUGLY_FOO_IMPL_HPP #include <memory> #include "fugly_foo.hpp" namespace fugly { template<typename T> class Foo<T>::Impl { public: HandlerFunc func_ = nullptr; }; template<typename T> typename Foo<T>::Ptr Foo<T>::Init() { return Ptr(new Foo); } template<typename T> Foo<T>::Foo() : impl_{new Impl{}} { } } // namespace fugly #endif // FUGLY_FOO_IMPL_HPP

GNUmakefile

我需要使用CXX=clang++ CXXFLAGS=-std=c++11 -stdlib=libc++ -Wall -Werror -pedantic SRCS=idal_bar.cpp ideal_foo.cpp fugly_bar.cpp fugly_foo.cpp %.o : %.cpp %.hpp ${CXX} ${CXXFLAGS} -c -o $@ $< all: fugly ideal clean:: rm -rf $(SRCS:.cpp=.o) fugly ideal fugly: fugly_bar.o fugly_foo.o ${CXX} ${CXXFLAGS} -o $@ $^ ideal: ideal_bar.o ideal_foo.o ${CXX} ${CXXFLAGS} -o $@ $^ 模式来公开_impl.hpp的实际定义以及私有实现的其他内部结构(例如Init()),这会冒犯我的风格感和我'我想知道这是否真的像它一样好吗?我能说什么,C ++ 11非常干净,像这样的偏差似乎让我做错了。

因为这最初只是一个警告,似乎有一些语法可以用来推迟解决链接时间,但是由于使用了匿名命名空间并在这里使用了PIMPL惯用语,我不乐观。

感觉应该有一种方法可以显式实例化Impl或任何其他需要内部链接的函数,而不会将定义暴露给Init()。可能使用bar.cppextern的某种组合,但同样,匿名命名空间和template的定义隐藏在Init()中,这可能是唯一的方法。

我希望我错了,但遗憾的是不这么认为。

0 个答案:

没有答案