问题描述
解决方案可能微不足道,但是我找不到。我试图用Google搜索它,但是没有运气。
我正在在Linux上使用g ++进行C ++项目(gcc版本10.1.0 Ubuntu 10.1.0-2ubuntu1-18.04)。
g ++将C ++文件编译为对象.o,而不会引发任何错误,但是最终对象文件缺少功能!我写的其他8个库文件都已编译并链接良好,仅此一个给我带来了麻烦。为什么,如何解决?
库头文件 bpo_interface.h 是:
#pragma once
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/algorithm/string.hpp>
#include <optional>
#include <string>
namespace bpo = boost::program_options;
namespace ibsimu_client::bpo_interface {
template <typename T>
std::optional<T> get(bpo::variables_map ¶ms_op,std::string key)
}
bpo_interface.cpp :
#include "bpo_interface.h"
namespace ic_bpo = ibsimu_client::bpo_interface;
template <typename T>
std::optional<T> ic_bpo::get(bpo::variables_map ¶ms_op,std::string key)
{
try {
const T& value =
params_op[key].as<T>();
return value;
}
catch(const std::exception& e) {
return std::nullopt;
}
return std::nullopt;
}
用于编译文件的 g ++命令:
g++-10 -std=c++20 -lboost_program_options -Wall -g `pkg-config --cflags ibsimu-1.0.6dev` -c -o bin/build/bpo_interface.o src/bpo_interface.cpp
和 objdump -t -C bin / build / bpo_interface.o 的输出:
bin/build/bpo_interface.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 bpo_interface.cpp
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .rodata 0000000000000000 .rodata
0000000000000000 l O .rodata 0000000000000001 __pstl::execution::v1::seq
0000000000000001 l O .rodata 0000000000000001 __pstl::execution::v1::par
0000000000000002 l O .rodata 0000000000000001 __pstl::execution::v1::par_unseq
0000000000000003 l O .rodata 0000000000000001 __pstl::execution::v1::unseq
0000000000000004 l O .rodata 0000000000000004 __gnu_cxx::__default_lock_policy
0000000000000008 l O .rodata 0000000000000008 boost::container::ADP_nodes_per_block
0000000000000010 l O .rodata 0000000000000008 boost::container::ADP_max_free_blocks
0000000000000018 l O .rodata 0000000000000008 boost::container::ADP_overhead_percent
0000000000000020 l O .rodata 0000000000000008 boost::container::ADP_only_alignment
0000000000000028 l O .rodata 0000000000000008 boost::container::NodeAlloc_nodes_per_block
0000000000000030 l O .rodata 0000000000000001 boost::container::ordered_range
0000000000000031 l O .rodata 0000000000000001 boost::container::ordered_unique_range
0000000000000032 l O .rodata 0000000000000001 boost::container::default_init
0000000000000033 l O .rodata 0000000000000001 boost::container::value_init
0000000000000000 l d .debug_info 0000000000000000 .debug_info
0000000000000000 l d .debug_abbrev 0000000000000000 .debug_abbrev
0000000000000000 l d .debug_aranges 0000000000000000 .debug_aranges
0000000000000000 l d .debug_line 0000000000000000 .debug_line
0000000000000000 l d .debug_str 0000000000000000 .debug_str
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .comment 0000000000000000 .comment
与objdump结果一致,链接器抱怨找不到ic_bpo :: get()函数-具体地说:
undefined reference to 'std::optional<std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char> > > ibsimu_client::bpo_interface::get<std::__cxx11::basic_string<char,std::allocator<char> > >(boost::program_options::variable_maps&,std::__cxx11::basic_string<char,std::allocator<char> >)
如果我将函数主体复制并粘贴到定义文件bpo_interface.h中,然后从项目中删除bpo_interface.cpp和bpo_interface.o,一切正常。
所以我想g ++在编译时完全可以处理该函数,并将其声明与项目中的用法匹配。
但是为什么不将其编译到bpo_interface.o对象文件中?
谢谢
解决方法
您已将模板功能get()
的定义放置在源(cpp
)文件中。这意味着在需要为该源文件之外的特定专业化功能模板实例化时,该定义不可用。请注意,功能模板的定义不等同于非功能模板的定义,它更像是如何为特定专业生成定义的蓝图;隐式或显式实例化(定义)所需。
将定义移至头文件时,可以在实例化特定专业化时根据需要随时使用功能模板定义。您可以 将功能模板的定义放在源文件中,但是由于该源文件是唯一可以看到这些定义的翻译单元,因此,您还需要提供所有实例的显式实例化定义。您想要模板函数为其提供实例化定义的专业化。这是非常罕见的,通常仅用于静态依赖注入到类模板中,该模板仅具有一个用于生产代码意图的专门化(然后可以显式实例化),例如测试代码的其他实例化(例如,注入模拟的或存根的实现)。
但是为什么不将其编译到bpo_interface.o对象文件中?
来自cppreference - function templates [强调我的]:
,功能模板实例化
功能模板本身不是类型,功能或任何其他实体。 从仅包含模板定义的源文件中不会生成任何代码。为了显示任何代码,必须实例化一个模板:必须确定模板参数,以便编译器可以生成实际的函数(或从类模板生成类)。
tl; dr:将以下内容添加到您的model.predict_classes()
文件中:
model.predict_generator()
(当然,请确保包含.cpp
标头。)
但是为什么不将其编译到
template std::optional<std::string> ic_bpo::get<std::string(bpo::variables_map &,std::string);
对象文件中?
因为您定义了模板;您根本没有实例化该模板。只有实例化才是实际函数,可以将其编译并放入目标文件中。因此,您需要强制实例化模板。对于<string>
,这就是上面的代码。
或者,如果您将模板定义保留在标题中,则其他翻译单元可以根据需要自己实例化它。
另请参阅: