C++ 具有共享私有数据的模板函数
我在以下情况下寻找“最佳实践”: 一般来说,两个(或多个)非成员函数之间共享私有数据的方法有三种,各有优缺点:C++ 具有共享私有数据的模板函数,c++,templates,static,private,C++,Templates,Static,Private,我在以下情况下寻找“最佳实践”: 一般来说,两个(或多个)非成员函数之间共享私有数据的方法有三种,各有优缺点: // Example 1: using 'static' class // HPP namespace foo { class bar { private: static const char* const s_private; bar(); public: static void s_method0(
// Example 1: using 'static' class
// HPP
namespace foo {
class bar
{
private:
static const char* const s_private;
bar();
public:
static void s_method0();
static void s_method1();
}; /* class bar */
} /* namespace foo */
// CPP
namespace foo {
const char* const bar::s_private = "why do I need to be visible in HPP?";
void bar::s_method0() { std::cout << "s_method0 said: " << s_private << std::endl; }
void bar::s_method1() { std::cout << "s_method1 said: " << s_private << std::endl; }
} /* namespace foo */
// Example 2: using unnamed-namespace
// HPP
namespace foo {
void bar0();
void bar1();
} /* namespace foo */
// CPP
namespace foo {
namespace {
const char* const g_anonymous = "why do I need external linkage?";
} /* unnamed-namespace */
void bar0() { std::cout << "bar0 said: " << g_anonymous << std::endl; }
void bar1() { std::cout << "bar1 said: " << g_anonymous << std::endl; }
} /* namespace foo */
// Example 3: using static keyword in namespace-scope
// HPP
namespace foo {
void bar0();
void bar1();
} /* namespace foo */
// CPP
namespace foo {
static const char* const g_internal = "nobody outside this file can see me and I don't have external linkage";
void bar0() { std::cout << "bar0 said: " << g_internal << std::endl; }
void bar1() { std::cout << "bar1 said: " << g_internal << std::endl; }
} /* namespace foo */
//示例1:使用“static”类
//水电站
名称空间foo{
分类栏
{
私人:
静态常量char*常量s_private;
bar();
公众:
静态void s_method0();
静态void s_method1();
}/*类栏*/
}/*名称空间foo*/
//CPP
名称空间foo{
const char*const bar::s_private=“为什么我需要在HPP中可见?”;
void bar::s_method0(){std::cout那怎么办
namespace foo {
namespace detail {
class shared
{
template<typename> friend void bar0();
template<typename> friend void bar1();
static const char* const m_private;
}; /* class shared */
} /* namespace detail */
template<typename T> void bar0() { std::cout << "bar0 said: " << detail::shared::m_private << std::endl; }
template<typename T> void bar1() { std::cout << "bar1 said: " << detail::shared::m_private << std::endl; }
} /* namespace foo */
名称空间foo{
名称空间详细信息{
类共享
{
模板0();
模板1();
静态常量char*常量m_private;
};/*类共享*/
}/*名称空间详细信息*/
模板void bar0(){std::cout这是怎么回事
namespace foo {
namespace detail {
class shared
{
template<typename> friend void bar0();
template<typename> friend void bar1();
static const char* const m_private;
}; /* class shared */
} /* namespace detail */
template<typename T> void bar0() { std::cout << "bar0 said: " << detail::shared::m_private << std::endl; }
template<typename T> void bar1() { std::cout << "bar1 said: " << detail::shared::m_private << std::endl; }
} /* namespace foo */
名称空间foo{
名称空间详细信息{
类共享
{
模板0();
模板1();
静态常量char*常量m_private;
};/*类共享*/
}/*名称空间详细信息*/
template void bar0(){std::cout如果我理解正确,您的抱怨/担忧是,与模板不同,对于非模板,可以在CPP中定义函数体,而不是标头,在这种情况下,它们可以访问“不可见”的非类静态对于外部世界,包括在标题中定义的成员函数。这都是真的
但是,请记住,在CPP中定义其他成员函数也没有什么可以阻止的,在这种情况下,它们同样能够访问静态数据。因此,事实上,情况没有什么不同。您的投诉基于错误的二分法
如果你真的不想阻止任何事情,除了s_method0()
和s_method1()
访问s_private
,然后你必须将它们都放在一个专用类中。就这么简单。即使是非模板也是如此。如果我理解正确,你的抱怨/担心是,与模板不同,非模板可以在CPP中定义函数体,而不是标题在这种情况下,他们可以访问外部世界“看不见”的非类静态,包括头中定义的成员函数
但是,请记住,在CPP中定义其他成员函数也没有什么可以阻止的,在这种情况下,它们同样能够访问静态数据。因此,事实上,情况没有什么不同。您的投诉基于错误的二分法
如果你真的不想阻止任何事情,除了s_method0()
和s_method1()
访问s_private
,然后你必须将它们都放在一个专用类中。就这么简单。即使是非模板也是如此。我已经使用了不同的技术。我的想法是,在头文件中使用未命名的名称空间,将“共享”类“标记”为“仅头文件”。当然,因为事实上,他们不包含公众成员,你无论如何也不能用它来制造令人讨厌的事情。但我认为这会更接近你的意图
但我错了!考虑到这一点后,我感到羞愧。它在逻辑上非常简单。这个示例显示了它的问题所在(为了清晰起见,整个代码):
这意味着:如果你正在寻找未定义的行为,那么你就在这里。
换句话说:基于上面的解释:不要在头文件中使用未命名的名称空间来封装私有共享数据
最后一个问题是,“解决方案是什么?”
如果您不想使用“静态”(实用程序)类,您应该更喜欢我第一次发布的解决方案(仅更改代码):
//header0.hpp
// ...
名称空间ns{
// ...
名称空间详细信息{
班长0{/*…*/};
}/*名称空间详细信息*/
模板无效标头0_func0(){std::cout我曾经尝试过不同的技术。我的想法是,在头文件中使用未命名的名称空间,将“共享”类“标记”为“仅头文件”。当然,由于它们不包含公共成员,所以无论如何也不能用它来制造麻烦。但我认为这更接近于目的
但我错了!考虑到这一点后,我感到羞愧。它在逻辑上非常简单。这个示例显示了它的问题所在(为了清晰起见,整个代码):
这意味着:如果你正在寻找未定义的行为,那么你就在这里。
换句话说:基于上面的解释:不要在头文件中使用未命名的名称空间来封装私有共享数据
最后一个问题是,“解决方案是什么?”
如果您不想使用“静态”(实用程序)类,您应该更喜欢我第一次发布的解决方案(仅更改代码):
//header0.hpp
// ...
名称空间ns{
// ...
名称空间详细信息{
班长0{/*…*/};
}/*名称空间详细信息*/
模板void header0_func0(){std::cout这是一个在模板中经常出现的问题,有点遗憾
但我可以建议你在这里工作吗
事实是,无论你看Loki代码(由Andrei Alexandrescu编写)还是Boost代码(臭名昭著的David Abrahams),都没有人真正愿意提供更好的隐私
相反,他们仅仅依赖于约定,使用私有
名称空间(Loki)或细节
名称空间(Boost,有时使用更长更具描述性的名称以防止冲突)
这很烦人,但在实践中你能做的并不多……尽管我实际上对你的具体问题有一个解决方案;)
//邪恶的解决方案!
#ifdef MY_SUPER_宏
#错误“我的超级宏已被定义”
namespace foo {
template<typename T> void bar0();
template<typename T> void bar1();
template<typename T> void bar2();
template<typename T> void bar3();
namespace {
class shared0
{
template<typename> friend void foo::bar0();
template<typename> friend void foo::bar1();
static const char* const m_private0;
}; /* class shared0 */
class shared1
{
template<typename> friend void foo::bar2();
template<typename> friend void foo::bar3();
static const char* const m_private1;
}; /* class shared1 */
} /* unnamed */
template<typename T> void bar0() { std::cout << "bar0 said: " << shared0::m_private0 << std::endl; }
template<typename T> void bar1() { std::cout << "bar1 said: " << shared0::m_private0 << std::endl; }
template<typename T> void bar2() { std::cout << "bar0 said: " << shared1::m_private1 << std::endl; }
template<typename T> void bar3() { std::cout << "bar1 said: " << shared1::m_private1 << std::endl; }
} /* namespace foo */
// header0.hpp
#ifndef HPP_HEADER0_INCLUDED
#define HPP_HEADER0_INCLUDED
#include <iostream>
#include <string>
namespace ns {
template<typename T> void header0_func0();
template<typename T> void header0_func1();
namespace {
class header0
{
template<typename> friend void ns::header0_func0();
template<typename> friend void ns::header0_func1();
static std::string s_private;
}; /* class header0 */
} /* unnamed */
template<typename T> void header0_func0() { std::cout << "header0_func0: " << header0::s_private << std::endl; }
template<typename T> void header0_func1() { std::cout << "header0_func1: " << header0::s_private << std::endl; }
} /* namespace ns */
#endif /* HPP_HEADER0_INCLUDED */
// header1.hpp
#ifndef HPP_HEADER1_INCLUDED
#define HPP_HEADER1_INCLUDED
#include <iostream>
#include <string>
namespace ns {
template<typename T> void header1_func0();
template<typename T> void header1_func1();
namespace {
class header1
{
template<typename> friend void ns::header1_func0();
template<typename> friend void ns::header1_func1();
static std::string s_private;
}; /* class header1 */
} /* unnamed */
template<typename T> void header1_func0() { std::cout << "header1_func0: " << header1::s_private << std::endl; }
template<typename T> void header1_func1() { std::cout << "header1_func0: " << header1::s_private << std::endl; }
} /* namespace ns */
#endif /* HPP_HEADER1_INCLUDED */
// source.cpp
#include "header0.hpp"
#include "header1.hpp"
std::string ns::header0::s_private = "header0 private data definition by source.cpp",
ns::header1::s_private = "header1 private data definition by source.cpp";
namespace {
// hide private class
class source
{
source();
~source();
static source s_instance;
};
source::source() {
std::cout << "source.cpp:\n";
ns::header0_func0<int>();
ns::header0_func1<int>();
ns::header1_func0<int>();
ns::header1_func1<int>();
std::cout << '\n';
}
source::~source() { }
source source::s_instance;
} /* unnamed */
// main.cpp
#include "header0.hpp"
#include "header1.hpp"
int main()
{
std::cout << "main.cpp:\n";
ns::header0_func0<int>();
ns::header0_func1<int>();
ns::header1_func0<int>();
ns::header1_func1<int>();
std::cout << '\n';
return 0;
}
// main.cpp
#include "header0.hpp"
#include "header1.hpp"
std::string ns::header0::s_private = "header0 private data definition by main.cpp",
ns::header1::s_private = "header1 private data definition by main.cpp";
int main()
{
std::cout << "main.cpp:\n";
ns::header0_func0<int>();
ns::header0_func1<int>();
ns::header1_func0<int>();
ns::header1_func1<int>();
std::cout << '\n';
return 0;
}
source.cpp:
header0_func0: header0 private data definition by source.cpp
header0_func1: header0 private data definition by source.cpp
header1_func0: header1 private data definition by source.cpp
header1_func0: header1 private data definition by source.cpp
main.cpp:
header0_func0: header0 private data definition by source.cpp
header0_func1: header0 private data definition by source.cpp
header1_func0: header1 private data definition by source.cpp
header1_func0: header1 private data definition by source.cpp
// header0.hpp
// ...
namespace ns {
// ...
namespace detail {
class header0 { /*...*/ };
} /* namespace detail */
template<typename T> void header0_func0() { std::cout << "header0_func0: " << detail::header0::s_private << std::endl; }
template<typename T> void header0_func1() { std::cout << "header0_func1: " << detail::header0::s_private << std::endl; }
} /* namespace ns */
// ...
// header1.hpp
// ...
namespace ns {
// ...
namespace detail {
class header1 { /*...*/ };
} /* namespace detail */
template<typename T> void header0_func0() { std::cout << "header0_func0: " << detail::header1::s_private << std::endl; }
template<typename T> void header0_func1() { std::cout << "header0_func1: " << detail::header1::s_private << std::endl; }
} /* namespace ns */
// ...
// source.cpp
// ...
std::string ns::detail::header0::s_private = "header0 private data definition by source.cpp",
ns::detail::header1::s_private = "header1 private data definition by source.cpp";
// ...
// Evil solution!
#ifdef MY_SUPER_MACRO
# error "MY_SUPER_MACRO is already defined!"
#endif
#define MY_SUPER_MACRO "Some string"
template <typename T> void foo() { std::cout << "foo - " MY_SUPER_MACRO "\n"; }
template <typename T> void bar() { std::cout << "bar - " MY_SUPER_MACRO "\n"; }
#undef MY_SUPER_MACRO