The original version of the C++ interface heavily used implicit constructors and conversion operators. This allowed, for example:
PREDICATE(hello, 1)
{ cout << "Hello " << (char *)A1 << endl; // Deprecated
  return true;
}
PREDICATE(add, 3)
{ return A3 = (long)A1 + (long)A2; // Deprecated
}
Version 2 is a bit more verbose:
PREDICATE(hello, 1)
{ cout << "Hello " << A1.as_string() << endl;
  return true;
}
PREDICATE(add, 3)
{ return A3.unify_int(A1.as_long() + A2.as_long());
}
There are a few reasons for this:
bool. In addition, the result 
of unification should always be checked (e.g., an “always succeed” unification 
could fail due to an out-of-memory error); the unify_XXX() 
methods return a bool and they can be wrapped inside a PlCheckFail() 
to raise an exception on unification failure.(char*)A1 becomes the more verbose
static_cast<std::string>(A1), which is longer than
A1.as_string(). 
Also, the string casts don't allow for specifying encoding.PlTerm t; Pl_put_atom_chars(t, "someName");
whereas this is now required:
PlTerm t; Pl_put_atom_chars(t.as_term_t(), "someName");
However, this is mostly avoided by methods and constructors that wrap the foreign language functions:
PlTerm_atom t("someName");
or
auto t = PlTerm_atom("someName");
Additionally, there are now wrappers for most of the PL_*() functions that check the error return and throw a C++ exception as appropriate.
Over time, it is expected that some of these restrictions will be eased, to allow a more compact coding style that was the intent of the original API. However, too much use of overloaded methods/constructors, implicit conversions and constructors can result in code that's difficult to understand, so a balance needs to be struck between compactness of code and understandability.
For backwards compatibility, much of the version 1 interface is still available (except for the implicit constructors and operators), but marked as “deprecated” ; code that depends on the parts that have been removed can be easily changed to use the new interface.
The version API often used char* for both setting and 
setting string values. This is not a problem for setting (although 
encodings can be an issue), but can introduce subtle bugs in the 
lifetimes of pointers if the buffer stack isn't used properly. PlStringBuffers 
makes the buffer stack easier to use, but it would be preferable to 
avoid its use altogether. C++, unlike C, has a standard string that 
allows easily keeping a copy rather than dealing with a pointer that 
might become invalid. (Also, C++ strings can contain null characters.)
C++ has default conversion operators from char* to
std::string, so some of the API support only
std::string, even though this can cause a small 
inefficiency. If this proves to be a problem, additional overloaded 
functions and methods can be provided in future (note that some 
compilers have optimizations that reduce the overheads of using
std::string); but for performance-critical code, the C 
functions can still be used.
There still remains the problems of Unicode and encodings.
std::wstring is one way of dealing with this. And for 
interfaces that use std::string, an encoding can be 
specified.26As of 2023-04, this 
had only been partially implemented. Some of the details 
for this - such as the default encoding - may change slightly in the 
future.