0%

Static Polymorphism in C++ Part 2

Function template overload

拿Boost.Serialization當範例

1
2
3
4
5
6
7
namespace boost::serialization {
template <class Archive>
void serialize(Archive& ar, two_things& tt, unsigned int version) {
ar & tt.a;
ar & tt.b;
}
}

Pros:

  • 就像膠水,將黏貼部分獨立成單一Unit,需要的時候再引入即可

Cons:

  • 跟前個方式依樣,需要打開library的namespace
  • 跟前一個方式依樣,存在Name Collisions的可能

Friend member function

又是Boost.Serialization

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <boost/serialization/serialization.hpp>
struct two_things {
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, unsigned int version) {
ar & a;
ar & b;
}
public:
int a;
bool b;
};

優缺點正好跟上面相反

Pros:

  • 不需要打開library的namespace
  • 不會造成Name Collisions

Cons:

  • Tightly coupled with structure

當我們如果不需要serialization功能時,透過上面的方式,我們只需要不include implemtation unit即可,不過這方法就不行了
另外一點,這方式引入了boost::serialization的依賴,不管我們需不需要,都必須承受這副作用

ARGUMENT-DEPENDENT LOOKUP

C++最難理解的特性之一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
namespace {
struct two_things {
int a;
bool b;
};
void do_something(two_things &t) {
printf("do_something in anonymous namespace\n");
}
};

int main()
{
two_things t;
do_something(t);
}
Worng usage example of ADL (std::swap)

What’s wrong with swap
看看以下的程式馬

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
namespace A {
struct two_things {
int a;
bool b;
};
void swap(two_things &, two_things &)
{
printf("swap in namespace A\n");
}
};
namespace B {
template <typename T>
void test(T& a, T&b)
{
swap(a, b);
}
}
int main()
{
A::two_things a, b;
int c, d;
B::test(a, b);
B::test(c, d);
}
  • swap(a, b) // invokes ADL because call name is unqualified
  • std::swap(a, b) // does not invoke ADL because call name is qualified

如果我們把swap改成std::stap,自訂億的swap就毫無作用了
正確的做法應該是

1
2
3
4
5
6
7
8
namespace B {
template <typename T>
void test(T& a, T&b)
{
using std::swap;
swap(a, b);
}
}
Correct usage example of ADL (std::swap)
  • Create a callable function object which does the two-step with an internal detail namespace’s swap.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    namespace nonstd {
    namespace detail {
    template <typename T>
    void swap(T&, T&) { printf("nonstd::swap\n"); }
    struct swap_func {
    template <typename T>
    void operator()(T &a, T&b) const noexcept {
    swap(a, b);
    }
    };
    }
    inline constexpr const auto swap = detail::swap_func{};
    }

    namespace B {
    template <typename T>
    void test(T& a, T&b)
    {
    nonstd::swap(a, b);
    }
    }
    這做法在Reference中的文章有特別說明,被稱作Customization Point Object
    亦或是niebloid(以作者的名稱命名的單字)
Friend or not?

假設我們把 namespace A中的swap從free_function變成friend function

1
2
3
4
5
6
7
8
struct two_things {
int a;
bool b;
friend void swap(two_things &, two_things &)
{
printf("swap in namespace A\n");
}
};

兩者是等價的,不過

  • friend function 可以存取 structure的private field,free function不能
  • friend function只能被ADL找到
Downgrade to C++11

inline variable是C++17才有的玩意 在有些時候用不了新標準的情況之下 有必要找個方法向下相容

1
2
3
4
5
6
7
8
namespace nonstd {
// older standards:
template <typename T>
struct __static_const { static constexpr T value{}; };
template <typename T>
constexpr T __static_const<T>::value;
constexpr const auto& swap = __static_const<detail::swap_func>::value;
}

Reference