一个库中的函数与另一个库中的模板匹配

时间:2016-11-18 21:11:21

标签: c++ templates c++14 spdlog

我正在开发一个使用两个不同库的C ++项目:spdlog用于记录,mutils-serialization用于将对象序列化为字节(用于通过网络发送)。两个库都正确使用命名空间,但是当我尝试编写同时使用它们的程序时,我的编译器(g ++ 6.2)给了我无意义的错误,这些错误似乎表明它试图从spdlog库中实例化一个函数模板通过使用mutils库中函数模板的定义。

这是我的简单测试程序:

DEBUG: org.springframework.web.servlet.DispatcherServlet - Rendering view [org.springframework.web.servlet.view.InternalResourceView: name 'index'; URL [WEB-INF/pages/index.jsp]] in DispatcherServlet with name 'dispatcher'
DEBUG: org.springframework.web.servlet.view.InternalResourceView - Forwarding to resource [WEB-INF/pages/index.jsp] in InternalResourceView 'index'
DEBUG: org.springframework.web.servlet.DispatcherServlet - Successfully completed request

#include <spdlog/spdlog.h> #include <spdlog/fmt/ostr.h> #include "TestSerializableObject.h" int main(int argc, char** argv) { auto global_logger = spdlog::rotating_logger_mt("global_logger", "log", 1024 * 1024 * 500, 3); global_logger->set_pattern("[%H:%M:%S.%e] [%l] %v"); global_logger->set_level(spdlog::level::trace); std::shared_ptr<spdlog::logger> logger(spdlog::get("global_logger")); auto message = std::make_shared<messaging::TestSerializableObject>( 1, 2, "A message!"); logger->trace("Received a message: {}", *message); } 是一个实现TestSerializableObject的简单类(启用序列化和拉入mutils-serialization库的接口),并提供mutils::ByteRepresentable(spdlog需要它)能够记录它)。如有必要,我可以发布代码。

当我使用operator<<编译时,我会收到这个冗长,丑陋的错误(不用担心,我会帮你解析它):

g++ -std=c++14 -I"./src" -I"./libraries" -I"./libraries/mutils/" -L"./libraries/" -O0 -g3 -Wall "src/LibraryCollisionTest.cpp"

关键是:

In file included from ./libraries/mutils/mutils.hpp:3:0,
                 from ./libraries/mutils-serialization/SerializationSupport.hpp:2,
                 from src/TestSerializableObject.h:10,
                 from src/LibraryCollisionTest.cpp:10:
./libraries/mutils/args-finder.hpp: In instantiation of ‘struct mutils::function_traits<messaging::TestSerializableObject>’:
./libraries/mutils/args-finder.hpp:75:41:   required from ‘auto mutils::convert(F) [with F = messaging::TestSerializableObject; ignore = void]’
./libraries/spdlog/fmt/bundled/format.h:1276:46:   required from ‘struct fmt::internal::ConvertToInt<messaging::TestSerializableObject>’
./libraries/spdlog/fmt/bundled/format.h:1485:5:   required by substitution of ‘template<class T> fmt::internal::MakeValue<Formatter>::MakeValue(const T&, typename fmt::internal::EnableIf<fmt::internal::Not<fmt::internal::ConvertToInt<T>::value>::value, int>::type) [with T = messaging::TestSerializableObject]’
./libraries/spdlog/fmt/bundled/format.h:2465:12:   required from ‘static fmt::internal::Value fmt::internal::ArgArray<N, true>::make(const T&) [with Formatter = fmt::BasicFormatter<char>; T = messaging::TestSerializableObject; unsigned int N = 1u]’
./libraries/spdlog/fmt/bundled/format.h:2898:5:   required from ‘void fmt::BasicWriter<Char>::write(fmt::BasicCStringRef<CharType>, const Args& ...) [with Args = {messaging::TestSerializableObject}; Char = char]’
./libraries/spdlog/details/logger_impl.h:69:9:   required from ‘void spdlog::logger::log(spdlog::level::level_enum, const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
./libraries/spdlog/details/logger_impl.h:127:5:   required from ‘void spdlog::logger::trace(const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
src/LibraryCollisionTest.cpp:21:53:   required from here
./libraries/mutils/args-finder.hpp:12:37: error: ‘operator()’ is not a member of ‘messaging::TestSerializableObject’
   : public function_traits<decltype(&T::operator())>
                                     ^~
./libraries/mutils/args-finder.hpp: In instantiation of ‘auto mutils::convert(F) [with F = messaging::TestSerializableObject; ignore = void]’:
./libraries/spdlog/fmt/bundled/format.h:1276:46:   required from ‘struct fmt::internal::ConvertToInt<messaging::TestSerializableObject>’
./libraries/spdlog/fmt/bundled/format.h:1485:5:   required by substitution of ‘template<class T> fmt::internal::MakeValue<Formatter>::MakeValue(const T&, typename fmt::internal::EnableIf<fmt::internal::Not<fmt::internal::ConvertToInt<T>::value>::value, int>::type) [with T = messaging::TestSerializableObject]’
./libraries/spdlog/fmt/bundled/format.h:2465:12:   required from ‘static fmt::internal::Value fmt::internal::ArgArray<N, true>::make(const T&) [with Formatter = fmt::BasicFormatter<char>; T = messaging::TestSerializableObject; unsigned int N = 1u]’
./libraries/spdlog/fmt/bundled/format.h:2898:5:   required from ‘void fmt::BasicWriter<Char>::write(fmt::BasicCStringRef<CharType>, const Args& ...) [with Args = {messaging::TestSerializableObject}; Char = char]’
./libraries/spdlog/details/logger_impl.h:69:9:   required from ‘void spdlog::logger::log(spdlog::level::level_enum, const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
./libraries/spdlog/details/logger_impl.h:127:5:   required from ‘void spdlog::logger::trace(const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
src/LibraryCollisionTest.cpp:21:53:   required from here
./libraries/mutils/args-finder.hpp:75:41: error: ‘as_function’ is not a member of ‘mutils::function_traits<messaging::TestSerializableObject>’
   return function_traits<F>::as_function(f);
          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
In file included from ./libraries/spdlog/fmt/fmt.h:21:0,
                 from ./libraries/spdlog/common.h:41,
                 from ./libraries/spdlog/spdlog.h:12,
                 from src/LibraryCollisionTest.cpp:8:
./libraries/spdlog/fmt/bundled/format.h: In instantiation of ‘struct fmt::internal::ConvertToInt<messaging::TestSerializableObject>’:
./libraries/spdlog/fmt/bundled/format.h:1485:5:   required by substitution of ‘template<class T> fmt::internal::MakeValue<Formatter>::MakeValue(const T&, typename fmt::internal::EnableIf<fmt::internal::Not<fmt::internal::ConvertToInt<T>::value>::value, int>::type) [with T = messaging::TestSerializableObject]’
./libraries/spdlog/fmt/bundled/format.h:2465:12:   required from ‘static fmt::internal::Value fmt::internal::ArgArray<N, true>::make(const T&) [with Formatter = fmt::BasicFormatter<char>; T = messaging::TestSerializableObject; unsigned int N = 1u]’
./libraries/spdlog/fmt/bundled/format.h:2898:5:   required from ‘void fmt::BasicWriter<Char>::write(fmt::BasicCStringRef<CharType>, const Args& ...) [with Args = {messaging::TestSerializableObject}; Char = char]’
./libraries/spdlog/details/logger_impl.h:69:9:   required from ‘void spdlog::logger::log(spdlog::level::level_enum, const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
./libraries/spdlog/details/logger_impl.h:127:5:   required from ‘void spdlog::logger::trace(const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
src/LibraryCollisionTest.cpp:21:53:   required from here
./libraries/spdlog/fmt/bundled/format.h:1276:38: warning: invalid application of ‘sizeof’ to a void type [-Wpointer-arith]
     enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) };

不知何故,g ++已经从spdlog库中的模板化函数(在命名空间./libraries/mutils/args-finder.hpp: In instantiation of ‘auto mutils::convert(F) [with F = messaging::TestSerializableObject; ignore = void]’: ./libraries/spdlog/fmt/bundled/format.h:1276:46: required from ‘struct fmt::internal::ConvertToInt<messaging::TestSerializableObject>’ ./libraries/spdlog/fmt/bundled/format.h:1485:5: required by substitution of ‘template<class T> fmt::internal::MakeValue<Formatter>::MakeValue(const T&, typename fmt::internal::EnableIf<fmt::internal::Not< fmt::internal::ConvertToInt<T>::value>::value, int>::type) [with T = messaging::TestSerializableObject]’ 中)扩展到mutils库中的函数模板,在命名空间fmt::internal中,这显然不是spdlog图书馆打算做!如果我查看mutils的第1276行,那就是调用&#34;转换&#34;此模板struct中的函数:

format.h

以上几行,当然,功能是&#34;转换&#34;:

template<typename T>
struct ConvertToInt
{
    enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) };
    enum { value = ConvertToIntImpl2<T, enable_conversion>::value };
};

这些都在命名空间template <typename T> T &get(); Yes &convert(fmt::ULongLong); No &convert(...); 内,我的IDE同意如果我想要定义函数&#34;转换&#34;在1276行,我应该跳到函数&#34;转换&#34;在第1248行。那么为什么g ++会忽略这个定义,而是尝试使用fmt::internal的定义,即使在正确的命名空间中也是如此?

请注意,clang也无法编译该程序,并犯了同样的错误,所以我不认为这是g ++中的错误。

1 个答案:

答案 0 :(得分:9)

这绝对是spdlog中的一个错误。

本常见问题解答中概述问题
What is “Argument-Dependent Lookup” (aka ADL, or “Koenig Lookup”)?

因为messaging::TestSerializableObject继承自名称空间mutils中的某个类型,所以当convert从名称空间fmt::internal内部TestSerializableObject调用fmt::internal::convertmutils::convert F在重载集中考虑 ...。变量函数在重载决策期间始终排在最后,因此后者中的模板参数mutils::convert比前者中的convert更好匹配,并且选择了convert

这绝不是特定于您的代码或mutils - 在同一名称空间或父名称空间中具有名为fmt::internal::ConvertToInt<T>::enable_conversion的一元函数或函数模板的任何类型都容易受到此问题的影响。

修复是为了限定enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) }; 来电并更改enum { enable_conversion = sizeof(internal::convert(get<T>())) == sizeof(Yes) }; 的定义

internal

detail

在我自己的代码中,我习惯于始终限定所有.deletedText { color: red; text-decoration: line-through; } / <li>$ \cl "deletedText"{ax^2+bx+c=0}$</li> <li class="deletedText">$ax^2+bx+c=0$</li> 命名空间内的函数调用,甚至来自同一命名空间内的代码,除非ADL用法是明确的意图。 (N.b.调用不需要完全合格,只需要合格。)我从观看Boost必须以C ++ 11出现的艰难方式解决这个问题的过程中吸取了教训。 : - ]